Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 6 Aug 2011 20:54:36 +0000 (13:54 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 6 Aug 2011 20:54:36 +0000 (13:54 -0700)
* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6:
  cifs: cope with negative dentries in cifs_get_root
  cifs: convert prefixpath delimiters in cifs_build_path_to_root
  CIFS: Fix missing a decrement of inFlight value
  cifs: demote DFS referral lookup errors to cFYI
  Revert "cifs: advertise the right receive buffer size to the server"

566 files changed:
Documentation/ABI/testing/pstore
Documentation/ABI/testing/sysfs-platform-ideapad-laptop
Documentation/CodingStyle
Documentation/acpi/apei/einj.txt
Documentation/device-mapper/dm-crypt.txt
Documentation/device-mapper/dm-flakey.txt
Documentation/device-mapper/dm-raid.txt
Documentation/devicetree/bindings/gpio/gpio_keys.txt
Documentation/devicetree/bindings/input/fsl-mma8450.txt [new file with mode: 0644]
Documentation/dmaengine.txt
Documentation/fault-injection/fault-injection.txt
Documentation/feature-removal-schedule.txt
Documentation/frv/booting.txt
Documentation/ioctl/ioctl-number.txt
Documentation/kernel-parameters.txt
Documentation/m68k/kernel-options.txt
Documentation/networking/bonding.txt
Documentation/power/runtime_pm.txt
MAINTAINERS
arch/Kconfig
arch/alpha/Kconfig
arch/arm/kernel/process.c
arch/avr32/Kconfig
arch/cris/arch-v10/drivers/sync_serial.c
arch/cris/arch-v10/kernel/irq.c
arch/cris/include/asm/thread_info.h
arch/frv/Kconfig
arch/ia64/Kconfig
arch/ia64/include/asm/gpio.h [new file with mode: 0644]
arch/ia64/kernel/efi.c
arch/m68k/Kconfig
arch/parisc/Kconfig
arch/parisc/include/asm/atomic.h
arch/parisc/include/asm/futex.h
arch/parisc/include/asm/unistd.h
arch/parisc/kernel/syscall_table.S
arch/powerpc/Kconfig
arch/s390/Kconfig
arch/s390/include/asm/ipl.h
arch/s390/include/asm/lowcore.h
arch/s390/include/asm/processor.h
arch/s390/include/asm/system.h
arch/s390/kernel/asm-offsets.c
arch/s390/kernel/base.S
arch/s390/kernel/compat_signal.c
arch/s390/kernel/entry.S
arch/s390/kernel/entry64.S
arch/s390/kernel/ipl.c
arch/s390/kernel/reipl64.S
arch/s390/kernel/setup.c
arch/s390/kernel/signal.c
arch/s390/kernel/smp.c
arch/s390/mm/maccess.c
arch/s390/mm/pgtable.c
arch/sh/Kconfig
arch/sh/kernel/idle.c
arch/sparc/Kconfig
arch/sparc/include/asm/Kbuild
arch/sparc/include/asm/bitops_64.h
arch/sparc/include/asm/div64.h [deleted file]
arch/sparc/include/asm/elf_64.h
arch/sparc/include/asm/hypervisor.h
arch/sparc/include/asm/irq_regs.h [deleted file]
arch/sparc/include/asm/local.h [deleted file]
arch/sparc/include/asm/local64.h [deleted file]
arch/sparc/include/asm/tsb.h
arch/sparc/kernel/cpu.c
arch/sparc/kernel/ds.c
arch/sparc/kernel/entry.h
arch/sparc/kernel/head_64.S
arch/sparc/kernel/hvapi.c
arch/sparc/kernel/hvcalls.S
arch/sparc/kernel/kernel.h
arch/sparc/kernel/ktlb.S
arch/sparc/kernel/mdesc.c
arch/sparc/kernel/setup_64.c
arch/sparc/kernel/sparc_ksyms_64.c
arch/sparc/kernel/sstate.c
arch/sparc/kernel/unaligned_64.c
arch/sparc/kernel/vmlinux.lds.S
arch/sparc/lib/Makefile
arch/sparc/lib/NG2page.S [deleted file]
arch/sparc/lib/NGpage.S
arch/sparc/lib/atomic32.c
arch/sparc/lib/ffs.S [new file with mode: 0644]
arch/sparc/lib/hweight.S [new file with mode: 0644]
arch/sparc/mm/init_64.c
arch/tile/Kconfig
arch/tile/include/asm/Kbuild
arch/tile/include/asm/bug.h [deleted file]
arch/tile/include/asm/bugs.h [deleted file]
arch/tile/include/asm/cputime.h [deleted file]
arch/tile/include/asm/device.h [deleted file]
arch/tile/include/asm/div64.h [deleted file]
arch/tile/include/asm/emergency-restart.h [deleted file]
arch/tile/include/asm/errno.h [deleted file]
arch/tile/include/asm/fb.h [deleted file]
arch/tile/include/asm/fcntl.h [deleted file]
arch/tile/include/asm/fixmap.h
arch/tile/include/asm/ioctl.h [deleted file]
arch/tile/include/asm/ioctls.h [deleted file]
arch/tile/include/asm/ipc.h [deleted file]
arch/tile/include/asm/ipcbuf.h [deleted file]
arch/tile/include/asm/irq_regs.h [deleted file]
arch/tile/include/asm/kdebug.h [deleted file]
arch/tile/include/asm/local.h [deleted file]
arch/tile/include/asm/module.h [deleted file]
arch/tile/include/asm/msgbuf.h [deleted file]
arch/tile/include/asm/mutex.h [deleted file]
arch/tile/include/asm/param.h [deleted file]
arch/tile/include/asm/parport.h [deleted file]
arch/tile/include/asm/poll.h [deleted file]
arch/tile/include/asm/posix_types.h [deleted file]
arch/tile/include/asm/resource.h [deleted file]
arch/tile/include/asm/scatterlist.h [deleted file]
arch/tile/include/asm/sembuf.h [deleted file]
arch/tile/include/asm/serial.h [deleted file]
arch/tile/include/asm/shmbuf.h [deleted file]
arch/tile/include/asm/shmparam.h [deleted file]
arch/tile/include/asm/socket.h [deleted file]
arch/tile/include/asm/sockios.h [deleted file]
arch/tile/include/asm/statfs.h [deleted file]
arch/tile/include/asm/termbits.h [deleted file]
arch/tile/include/asm/termios.h [deleted file]
arch/tile/include/asm/types.h [deleted file]
arch/tile/include/asm/ucontext.h [deleted file]
arch/tile/include/asm/xor.h [deleted file]
arch/tile/include/hv/drv_srom_intf.h [new file with mode: 0644]
arch/tile/kernel/time.c
arch/tile/mm/init.c
arch/x86/Kconfig
arch/x86/include/asm/io.h
arch/x86/include/asm/processor.h
arch/x86/kernel/acpi/cstate.c
arch/x86/kernel/process.c
arch/x86/kernel/process_32.c
arch/x86/kernel/process_64.c
arch/x86/platform/mrst/Makefile
arch/x86/platform/mrst/pmu.c [new file with mode: 0644]
arch/x86/platform/mrst/pmu.h [new file with mode: 0644]
arch/x86/xen/Makefile
arch/x86/xen/setup.c
arch/x86/xen/trace.c
block/blk-core.c
block/blk-timeout.c
drivers/acpi/acpica/acglobal.h
drivers/acpi/acpica/aclocal.h
drivers/acpi/acpica/acpredef.h
drivers/acpi/acpica/nspredef.c
drivers/acpi/acpica/nsrepair2.c
drivers/acpi/acpica/tbinstal.c
drivers/acpi/apei/Kconfig
drivers/acpi/apei/apei-base.c
drivers/acpi/apei/apei-internal.h
drivers/acpi/apei/einj.c
drivers/acpi/apei/erst-dbg.c
drivers/acpi/apei/erst.c
drivers/acpi/apei/ghes.c
drivers/acpi/apei/hest.c
drivers/acpi/battery.c
drivers/acpi/bus.c
drivers/acpi/dock.c
drivers/acpi/ec_sys.c
drivers/acpi/fan.c
drivers/acpi/osl.c
drivers/acpi/pci_irq.c
drivers/acpi/pci_root.c
drivers/acpi/processor_thermal.c
drivers/acpi/sbs.c
drivers/acpi/sleep.c
drivers/acpi/sysfs.c
drivers/acpi/thermal.c
drivers/acpi/video.c
drivers/ata/libata-acpi.c
drivers/base/devtmpfs.c
drivers/base/power/domain.c
drivers/base/power/runtime.c
drivers/char/Kconfig
drivers/char/Makefile
drivers/char/ramoops.c
drivers/char/tile-srom.c [new file with mode: 0644]
drivers/char/tpm/tpm_tis.c
drivers/connector/cn_proc.c
drivers/cpuidle/cpuidle.c
drivers/cpuidle/cpuidle.h
drivers/cpuidle/driver.c
drivers/cpuidle/governor.c
drivers/dma/TODO
drivers/dma/amba-pl08x.c
drivers/dma/at_hdmac.c
drivers/dma/coh901318.c
drivers/dma/dmaengine.c
drivers/dma/ep93xx_dma.c
drivers/dma/imx-sdma.c
drivers/dma/intel_mid_dma.c
drivers/dma/ioat/dma_v3.c
drivers/dma/ioat/pci.c
drivers/dma/ipu/ipu_idmac.c
drivers/dma/mv_xor.c
drivers/dma/mxs-dma.c
drivers/dma/pch_dma.c
drivers/dma/pl330.c
drivers/dma/ste_dma40.c
drivers/dma/ste_dma40_ll.h
drivers/eisa/pci_eisa.c
drivers/firmware/efivars.c
drivers/gpu/drm/drm_debugfs.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_irq.c
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.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_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_panel.c
drivers/gpu/drm/i915/intel_ringbuffer.c
drivers/gpu/drm/radeon/Makefile
drivers/gpu/drm/radeon/atom.c
drivers/gpu/drm/radeon/evergreen_cs.c
drivers/gpu/drm/radeon/r600_cs.c
drivers/gpu/drm/radeon/radeon_combios.c
drivers/gpu/drm/radeon/radeon_connectors.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_i2c.c
drivers/gpu/drm/radeon/radeon_mode.h
drivers/ide/cy82c693.c
drivers/ide/ide_platform.c
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/lm8323.c
drivers/input/keyboard/tegra-kbc.c
drivers/input/misc/kxtj9.c
drivers/input/misc/mma8450.c
drivers/input/mouse/hgpk.c
drivers/input/serio/xilinx_ps2.c
drivers/input/touchscreen/ad7879.c
drivers/md/Kconfig
drivers/md/dm-crypt.c
drivers/md/dm-flakey.c
drivers/md/dm-io.c
drivers/md/dm-ioctl.c
drivers/md/dm-kcopyd.c
drivers/md/dm-log-userspace-base.c
drivers/md/dm-log.c
drivers/md/dm-mpath.c
drivers/md/dm-raid.c
drivers/md/dm-snap-persistent.c
drivers/md/dm-snap.c
drivers/md/dm-table.c
drivers/md/dm.c
drivers/md/dm.h
drivers/net/bnx2x/bnx2x_cmn.c
drivers/net/bnx2x/bnx2x_hsi.h
drivers/net/bnx2x/bnx2x_link.c
drivers/net/bnx2x/bnx2x_link.h
drivers/net/bnx2x/bnx2x_reg.h
drivers/net/e1000/e1000_ethtool.c
drivers/net/e1000/e1000_hw.c
drivers/net/e1000e/es2lan.c
drivers/net/e1000e/ethtool.c
drivers/net/e1000e/ich8lan.c
drivers/net/e1000e/lib.c
drivers/net/e1000e/netdev.c
drivers/net/e1000e/phy.c
drivers/net/igb/e1000_nvm.c
drivers/net/igb/igb_ethtool.c
drivers/net/igb/igb_main.c
drivers/net/igbvf/netdev.c
drivers/net/irda/smsc-ircc2.c
drivers/net/ixgb/ixgb_ee.c
drivers/net/ixgb/ixgb_hw.c
drivers/net/ixgbe/ixgbe_82599.c
drivers/net/ixgbe/ixgbe_common.c
drivers/net/ixgbe/ixgbe_ethtool.c
drivers/net/ixgbe/ixgbe_main.c
drivers/net/ixgbe/ixgbe_phy.c
drivers/net/ixgbe/ixgbe_x540.c
drivers/net/macb.c
drivers/net/mlx4/en_port.c
drivers/net/mlx4/main.c
drivers/net/mlx4/port.c
drivers/net/niu.c
drivers/net/r8169.c
drivers/net/sis190.c
drivers/net/usb/cdc_ncm.c
drivers/net/wireless/ath/ath9k/ar9002_hw.c
drivers/net/wireless/ath/ath9k/ar9003_hw.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/iwlegacy/iwl-3945.c
drivers/net/wireless/iwlegacy/iwl-4965.c
drivers/net/wireless/iwlwifi/iwl-5000.c
drivers/net/wireless/iwlwifi/iwl-core.h
drivers/net/wireless/iwlwifi/iwl-pci.c
drivers/net/wireless/iwlwifi/iwl-power.c
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2x00lib.h
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rtlwifi/pci.c
drivers/of/address.c
drivers/of/base.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/acer-wmi.c
drivers/platform/x86/acerhdf.c
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/asus-nb-wmi.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/asus-wmi.h
drivers/platform/x86/dell-laptop.c
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/eeepc-wmi.c
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel_ips.c
drivers/platform/x86/intel_menlow.c
drivers/platform/x86/intel_mid_thermal.c
drivers/platform/x86/intel_rar_register.c
drivers/platform/x86/intel_scu_ipc.c
drivers/platform/x86/msi-laptop.c
drivers/platform/x86/msi-wmi.c
drivers/platform/x86/samsung-laptop.c
drivers/platform/x86/samsung-q10.c [new file with mode: 0644]
drivers/platform/x86/thinkpad_acpi.c
drivers/regulator/core.c
drivers/regulator/dummy.c
drivers/regulator/tps65910-regulator.c
drivers/regulator/twl-regulator.c
drivers/regulator/wm831x-dcdc.c
drivers/regulator/wm831x-ldo.c
drivers/regulator/wm8994-regulator.c
drivers/rtc/rtc-omap.c
drivers/s390/block/dasd.c
drivers/s390/block/dasd_eckd.c
drivers/s390/block/dasd_proc.c
drivers/s390/char/sclp_async.c
drivers/s390/cio/qdio.h
drivers/s390/cio/qdio_debug.c
drivers/s390/cio/qdio_main.c
drivers/spi/spi-pl022.c
drivers/target/iscsi/Kconfig
drivers/target/iscsi/iscsi_target.c
drivers/target/iscsi/iscsi_target_configfs.c
drivers/target/iscsi/iscsi_target_nego.c
drivers/target/target_core_transport.c
drivers/target/tcm_fc/tcm_fc.h
drivers/target/tcm_fc/tfc_cmd.c
drivers/target/tcm_fc/tfc_io.c
drivers/thermal/Kconfig
drivers/thermal/thermal_sys.c
drivers/tty/serial/imx.c
drivers/video/backlight/Kconfig
drivers/video/backlight/aat2870_bl.c
drivers/video/savage/savagefb.h
drivers/watchdog/Kconfig
drivers/watchdog/nv_tco.c
drivers/watchdog/shwdt.c
drivers/xen/Kconfig
fs/9p/acl.c
fs/9p/acl.h
fs/9p/vfs_inode_dotl.c
fs/Kconfig
fs/block_dev.c
fs/btrfs/Makefile
fs/btrfs/acl.c
fs/btrfs/compression.c
fs/btrfs/ctree.h
fs/btrfs/dir-item.c
fs/btrfs/extent-tree.c
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/extent_map.c
fs/btrfs/file-item.c
fs/btrfs/file.c
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/ref-cache.c [deleted file]
fs/btrfs/ref-cache.h [deleted file]
fs/btrfs/root-tree.c
fs/btrfs/transaction.c
fs/btrfs/tree-log.c
fs/btrfs/volumes.c
fs/dcache.c
fs/ext2/acl.c
fs/ext3/acl.c
fs/ext4/Makefile
fs/ext4/acl.c
fs/ext4/balloc.c
fs/ext4/block_validity.c
fs/ext4/ext4.h
fs/ext4/extents.c
fs/ext4/fsync.c
fs/ext4/ialloc.c
fs/ext4/indirect.c [new file with mode: 0644]
fs/ext4/inode.c
fs/ext4/ioctl.c
fs/ext4/mballoc.c
fs/ext4/mballoc.h
fs/ext4/namei.c
fs/ext4/page-io.c
fs/ext4/resize.c
fs/ext4/super.c
fs/ext4/truncate.h [new file with mode: 0644]
fs/generic_acl.c
fs/gfs2/acl.c
fs/hppfs/hppfs.c
fs/inode.c
fs/jbd2/checkpoint.c
fs/jbd2/journal.c
fs/jffs2/acl.c
fs/jffs2/acl.h
fs/jffs2/fs.c
fs/jffs2/os-linux.h
fs/jfs/acl.c
fs/jfs/xattr.c
fs/namei.c
fs/nfs/nfs3acl.c
fs/nfs/nfs3proc.c
fs/ocfs2/acl.c
fs/posix_acl.c
fs/proc/base.c
fs/pstore/inode.c
fs/pstore/internal.h
fs/pstore/platform.c
fs/reiserfs/xattr_acl.c
fs/stack.c
fs/xfs/linux-2.6/xfs_acl.c
include/acpi/acpi_drivers.h
include/acpi/acpixf.h
include/acpi/apei.h
include/acpi/processor.h
include/drm/drm_crtc.h
include/drm/i915_drm.h
include/linux/acpi.h
include/linux/amba/pl08x.h
include/linux/bitmap.h
include/linux/cpuidle.h
include/linux/cryptohash.h
include/linux/device-mapper.h
include/linux/dm-ioctl.h
include/linux/dm-kcopyd.h
include/linux/efi.h
include/linux/fault-inject.h
include/linux/fs.h
include/linux/genalloc.h
include/linux/gfp.h
include/linux/idr.h
include/linux/input.h
include/linux/jbd2.h
include/linux/llist.h [new file with mode: 0644]
include/linux/memcontrol.h
include/linux/mfd/aat2870.h
include/linux/mm.h
include/linux/nfs_fs.h
include/linux/of.h
include/linux/pci_ids.h
include/linux/posix_acl.h
include/linux/pstore.h
include/linux/radix-tree.h
include/linux/regulator/consumer.h
include/linux/regulator/driver.h
include/linux/shmem_fs.h
include/linux/swapops.h
include/linux/thermal.h
include/net/cipso_ipv4.h
include/net/dst.h
include/net/netlabel.h
include/trace/events/ext4.h
include/trace/events/jbd2.h
init/main.c
ipc/shm.c
kernel/debug/gdbstub.c
kernel/debug/kdb/kdb_bt.c
kernel/debug/kdb/kdb_cmds
kernel/debug/kdb/kdb_debugger.c
kernel/debug/kdb/kdb_io.c
kernel/debug/kdb/kdb_main.c
kernel/debug/kdb/kdb_private.h
kernel/futex.c
kernel/kmod.c
kernel/lockdep.c
kernel/taskstats.c
lib/Kconfig
lib/Makefile
lib/bitmap.c
lib/fault-inject.c
lib/genalloc.c
lib/idr.c
lib/llist.c [new file with mode: 0644]
lib/radix-tree.c
lib/sha1.c
mm/failslab.c
mm/filemap.c
mm/memcontrol.c
mm/memory-failure.c
mm/mincore.c
mm/oom_kill.c
mm/page_alloc.c
mm/shmem.c
mm/slab.c
mm/swapfile.c
mm/truncate.c
net/atm/br2684.c
net/core/skbuff.c
net/ipv4/igmp.c
net/ipv4/ip_output.c
net/ipv4/route.c
net/ipv6/addrconf.c
net/ipv6/datagram.c
net/ipv6/ip6_fib.c
net/ipv6/ip6_output.c
net/ipv6/route.c
net/netfilter/ipvs/ip_vs_ctl.c
net/netlabel/Makefile
net/netlabel/netlabel_addrlist.c
net/netlabel/netlabel_addrlist.h
net/netlabel/netlabel_cipso_v4.c
net/netlabel/netlabel_cipso_v4.h
net/netlabel/netlabel_domainhash.c
net/netlabel/netlabel_domainhash.h
net/netlabel/netlabel_kapi.c
net/netlabel/netlabel_mgmt.c
net/netlabel/netlabel_mgmt.h
net/netlabel/netlabel_unlabeled.c
net/netlabel/netlabel_unlabeled.h
net/netlabel/netlabel_user.c
net/netlabel/netlabel_user.h
net/sched/sch_sfq.c
net/socket.c
net/sunrpc/xprt.c
net/wireless/nl80211.c
net/xfrm/xfrm_algo.c
security/selinux/hooks.c
security/selinux/include/netif.h
security/selinux/include/netlabel.h
security/selinux/include/netnode.h
security/selinux/include/netport.h
security/selinux/netif.c
security/selinux/netlabel.c
security/selinux/netnode.c
security/selinux/netport.c
security/selinux/selinuxfs.c
security/selinux/ss/ebitmap.c
security/selinux/ss/mls.c
security/selinux/ss/mls.h
security/selinux/ss/policydb.c
security/selinux/ss/services.c
security/smack/smack_lsm.c
sound/core/pcm_compat.c
sound/core/rtctimer.c
sound/pci/asihpi/hpidspcd.c
sound/pci/asihpi/hpioctl.c
sound/pci/rme9652/hdspm.c
sound/soc/txx9/txx9aclc.c
tools/power/x86/turbostat/turbostat.c
tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c

index ddf451ee2a08812eb16a28f3264618ca2e1e749b..ff1df4e3b05962741195df437d8ec2891f3e29af 100644 (file)
@@ -39,3 +39,9 @@ Description:  Generic interface to platform dependent persistent storage.
                multiple) files based on the record size of the underlying
                persistent storage until at least this amount is reached.
                Default is 10 Kbytes.
+
+               Pstore only supports one backend at a time. If multiple
+               backends are available, the preferred backend may be
+               set by passing the pstore.backend= argument to the kernel at
+               boot time.
+
index 807fca2ae2a424f844a42d5d9e22b2b0bfe13578..ff53183c3848d20ea357c276fb1bf930693cbd5c 100644 (file)
@@ -4,3 +4,20 @@ KernelVersion: 2.6.37
 Contact:       "Ike Panhc <ike.pan@canonical.com>"
 Description:
                Control the power of camera module. 1 means on, 0 means off.
+
+What:          /sys/devices/platform/ideapad/cfg
+Date:          Jun 2011
+KernelVersion: 3.1
+Contact:       "Ike Panhc <ike.pan@canonical.com>"
+Description:
+               Ideapad capability bits.
+               Bit 8-10: 1 - Intel graphic only
+                         2 - ATI graphic only
+                         3 - Nvidia graphic only
+                         4 - Intel and ATI graphic
+                         5 - Intel and Nvidia graphic
+               Bit 16: Bluetooth exist (1 for exist)
+               Bit 17: 3G exist (1 for exist)
+               Bit 18: Wifi exist (1 for exist)
+               Bit 19: Camera exist (1 for exist)
+
index fa6e25b94a54bfe1d39450cd9bc568c4043ddfa3..c940239d967811083278cf3c88c4342df18092ba 100644 (file)
@@ -80,22 +80,13 @@ available tools.
 The limit on the length of lines is 80 columns and this is a strongly
 preferred limit.
 
-Statements longer than 80 columns will be broken into sensible chunks.
-Descendants are always substantially shorter than the parent and are placed
-substantially to the right. The same applies to function headers with a long
-argument list. Long strings are as well broken into shorter strings. The
-only exception to this is where exceeding 80 columns significantly increases
-readability and does not hide information.
-
-void fun(int a, int b, int c)
-{
-       if (condition)
-               printk(KERN_WARNING "Warning this is a long printk with "
-                                               "3 parameters a: %u b: %u "
-                                               "c: %u \n", a, b, c);
-       else
-               next_statement;
-}
+Statements longer than 80 columns will be broken into sensible chunks, unless
+exceeding 80 columns significantly increases readability and does not hide
+information. Descendants are always substantially shorter than the parent and
+are placed substantially to the right. The same applies to function headers
+with a long argument list. However, never break user-visible strings such as
+printk messages, because that breaks the ability to grep for them.
+
 
                Chapter 3: Placing Braces and Spaces
 
index dfab71848dc8f55a099beae5fa6d1a148c50c2c7..5cc699ba5453479ea13a028236f0d568063edb1e 100644 (file)
@@ -48,12 +48,19 @@ directory apei/einj. The following files are provided.
 - param1
   This file is used to set the first error parameter value. Effect of
   parameter depends on error_type specified. For memory error, this is
-  physical memory address.
+  physical memory address.  Only available if param_extension module
+  parameter is specified.
 
 - param2
   This file is used to set the second error parameter value. Effect of
   parameter depends on error_type specified. For memory error, this is
-  physical memory address mask.
+  physical memory address mask.  Only available if param_extension
+  module parameter is specified.
+
+Injecting parameter support is a BIOS version specific extension, that
+is, it only works on some BIOS version.  If you want to use it, please
+make sure your BIOS version has the proper support and specify
+"param_extension=y" in module parameter.
 
 For more information about EINJ, please refer to ACPI specification
 version 4.0, section 17.5.
index 6b5c42dbbe841c8ec6ed1b608ac3bd77ffe60fa1..2c656ae43ba7f9907571c181ad4800ea5ceeaa9f 100644 (file)
@@ -4,7 +4,8 @@ dm-crypt
 Device-Mapper's "crypt" target provides transparent encryption of block devices
 using the kernel crypto API.
 
-Parameters: <cipher> <key> <iv_offset> <device path> <offset>
+Parameters: <cipher> <key> <iv_offset> <device path> \
+             <offset> [<#opt_params> <opt_params>]
 
 <cipher>
     Encryption cipher and an optional IV generation mode.
@@ -37,6 +38,24 @@ Parameters: <cipher> <key> <iv_offset> <device path> <offset>
 <offset>
     Starting sector within the device where the encrypted data begins.
 
+<#opt_params>
+    Number of optional parameters. If there are no optional parameters,
+    the optional paramaters section can be skipped or #opt_params can be zero.
+    Otherwise #opt_params is the number of following arguments.
+
+    Example of optional parameters section:
+        1 allow_discards
+
+allow_discards
+    Block discard requests (a.k.a. TRIM) are passed through the crypt device.
+    The default is to ignore discard requests.
+
+    WARNING: Assess the specific security risks carefully before enabling this
+    option.  For example, allowing discards on encrypted devices may lead to
+    the leak of information about the ciphertext device (filesystem type,
+    used space etc.) if the discarded blocks can be located easily on the
+    device later.
+
 Example scripts
 ===============
 LUKS (Linux Unified Key Setup) is now the preferred way to set up disk
index c8efdfd19a655d02bd14f67d41abf45e16d1e9fd..6ff5c2327227f2040e43ec2b74f97a2bd74ca11f 100644 (file)
@@ -1,17 +1,53 @@
 dm-flakey
 =========
 
-This target is the same as the linear target except that it returns I/O
-errors periodically.  It's been found useful in simulating failing
-devices for testing purposes.
+This target is the same as the linear target except that it exhibits
+unreliable behaviour periodically.  It's been found useful in simulating
+failing devices for testing purposes.
 
 Starting from the time the table is loaded, the device is available for
-<up interval> seconds, then returns errors for <down interval> seconds,
-and then this cycle repeats.
+<up interval> seconds, then exhibits unreliable behaviour for <down
+interval> seconds, and then this cycle repeats.
 
-Parameters: <dev path> <offset> <up interval> <down interval>
+Also, consider using this in combination with the dm-delay target too,
+which can delay reads and writes and/or send them to different
+underlying devices.
+
+Table parameters
+----------------
+  <dev path> <offset> <up interval> <down interval> \
+    [<num_features> [<feature arguments>]]
+
+Mandatory parameters:
     <dev path>: Full pathname to the underlying block-device, or a
                 "major:minor" device-number.
     <offset>: Starting sector within the device.
     <up interval>: Number of seconds device is available.
     <down interval>: Number of seconds device returns errors.
+
+Optional feature parameters:
+  If no feature parameters are present, during the periods of
+  unreliability, all I/O returns errors.
+
+  drop_writes:
+       All write I/O is silently ignored.
+       Read I/O is handled correctly.
+
+  corrupt_bio_byte <Nth_byte> <direction> <value> <flags>:
+       During <down interval>, replace <Nth_byte> of the data of
+       each matching bio with <value>.
+
+    <Nth_byte>: The offset of the byte to replace.
+               Counting starts at 1, to replace the first byte.
+    <direction>: Either 'r' to corrupt reads or 'w' to corrupt writes.
+                'w' is incompatible with drop_writes.
+    <value>: The value (from 0-255) to write.
+    <flags>: Perform the replacement only if bio->bi_rw has all the
+            selected flags set.
+
+Examples:
+  corrupt_bio_byte 32 r 1 0
+       - replaces the 32nd byte of READ bios with the value 1
+
+  corrupt_bio_byte 224 w 0 32
+       - replaces the 224th byte of REQ_META (=32) bios with the value 0
index 33b6b7071ac8d65e386aead1e4e6f6a916e9c502..2a8c11331d2d6e8a861d272546e8a7f7d9f02cf8 100644 (file)
-Device-mapper RAID (dm-raid) is a bridge from DM to MD.  It
-provides a way to use device-mapper interfaces to access the MD RAID
-drivers.
+dm-raid
+-------
 
-As with all device-mapper targets, the nominal public interfaces are the
-constructor (CTR) tables and the status outputs (both STATUSTYPE_INFO
-and STATUSTYPE_TABLE).  The CTR table looks like the following:
+The device-mapper RAID (dm-raid) target provides a bridge from DM to MD.
+It allows the MD RAID drivers to be accessed using a device-mapper
+interface.
 
-1: <s> <l> raid \
-2:      <raid_type> <#raid_params> <raid_params> \
-3:      <#raid_devs> <meta_dev1> <dev1> .. <meta_devN> <devN>
-
-Line 1 contains the standard first three arguments to any device-mapper
-target - the start, length, and target type fields.  The target type in
-this case is "raid".
-
-Line 2 contains the arguments that define the particular raid
-type/personality/level, the required arguments for that raid type, and
-any optional arguments.  Possible raid types include: raid4, raid5_la,
-raid5_ls, raid5_rs, raid6_zr, raid6_nr, and raid6_nc.  (raid1 is
-planned for the future.)  The list of required and optional parameters
-is the same for all the current raid types.  The required parameters are
-positional, while the optional parameters are given as key/value pairs.
-The possible parameters are as follows:
- <chunk_size>           Chunk size in sectors.
- [[no]sync]             Force/Prevent RAID initialization
- [rebuild <idx>]        Rebuild the drive indicated by the index
- [daemon_sleep <ms>]    Time between bitmap daemon work to clear bits
- [min_recovery_rate <kB/sec/disk>]      Throttle RAID initialization
- [max_recovery_rate <kB/sec/disk>]      Throttle RAID initialization
- [max_write_behind <sectors>]           See '-write-behind=' (man mdadm)
- [stripe_cache <sectors>]               Stripe cache size for higher RAIDs
-
-Line 3 contains the list of devices that compose the array in
-metadata/data device pairs.  If the metadata is stored separately, a '-'
-is given for the metadata device position.  If a drive has failed or is
-missing at creation time, a '-' can be given for both the metadata and
-data drives for a given position.
-
-NB. Currently all metadata devices must be specified as '-'.
-
-Examples:
-# RAID4 - 4 data drives, 1 parity
+The target is named "raid" and it accepts the following parameters:
+
+  <raid_type> <#raid_params> <raid_params> \
+    <#raid_devs> <metadata_dev0> <dev0> [.. <metadata_devN> <devN>]
+
+<raid_type>:
+  raid1                RAID1 mirroring
+  raid4                RAID4 dedicated parity disk
+  raid5_la     RAID5 left asymmetric
+               - rotating parity 0 with data continuation
+  raid5_ra     RAID5 right asymmetric
+               - rotating parity N with data continuation
+  raid5_ls     RAID5 left symmetric
+               - rotating parity 0 with data restart
+  raid5_rs     RAID5 right symmetric
+               - rotating parity N with data restart
+  raid6_zr     RAID6 zero restart
+               - rotating parity zero (left-to-right) with data restart
+  raid6_nr     RAID6 N restart
+               - rotating parity N (right-to-left) with data restart
+  raid6_nc     RAID6 N continue
+               - rotating parity N (right-to-left) with data continuation
+
+  Refererence: Chapter 4 of
+  http://www.snia.org/sites/default/files/SNIA_DDF_Technical_Position_v2.0.pdf
+
+<#raid_params>: The number of parameters that follow.
+
+<raid_params> consists of
+    Mandatory parameters:
+        <chunk_size>: Chunk size in sectors.  This parameter is often known as
+                     "stripe size".  It is the only mandatory parameter and
+                     is placed first.
+
+    followed by optional parameters (in any order):
+       [sync|nosync]   Force or prevent RAID initialization.
+
+       [rebuild <idx>] Rebuild drive number idx (first drive is 0).
+
+       [daemon_sleep <ms>]
+               Interval between runs of the bitmap daemon that
+               clear bits.  A longer interval means less bitmap I/O but
+               resyncing after a failure is likely to take longer.
+
+       [min_recovery_rate <kB/sec/disk>]  Throttle RAID initialization
+       [max_recovery_rate <kB/sec/disk>]  Throttle RAID initialization
+       [write_mostly <idx>]               Drive index is write-mostly
+       [max_write_behind <sectors>]       See '-write-behind=' (man mdadm)
+       [stripe_cache <sectors>]           Stripe cache size (higher RAIDs only)
+       [region_size <sectors>]
+               The region_size multiplied by the number of regions is the
+               logical size of the array.  The bitmap records the device
+               synchronisation state for each region.
+
+<#raid_devs>: The number of devices composing the array.
+       Each device consists of two entries.  The first is the device
+       containing the metadata (if any); the second is the one containing the
+       data.
+
+       If a drive has failed or is missing at creation time, a '-' can be
+       given for both the metadata and data drives for a given position.
+
+
+Example tables
+--------------
+# RAID4 - 4 data drives, 1 parity (no metadata devices)
 # No metadata devices specified to hold superblock/bitmap info
 # Chunk size of 1MiB
 # (Lines separated for easy reading)
+
 0 1960893648 raid \
         raid4 1 2048 \
         5 - 8:17 - 8:33 - 8:49 - 8:65 - 8:81
 
-# RAID4 - 4 data drives, 1 parity (no metadata devices)
+# RAID4 - 4 data drives, 1 parity (with metadata devices)
 # Chunk size of 1MiB, force RAID initialization,
 #       min recovery rate at 20 kiB/sec/disk
+
 0 1960893648 raid \
-        raid4 4 2048 min_recovery_rate 20 sync\
-        5 - 8:17 - 8:33 - 8:49 - 8:65 - 8:81
+        raid4 4 2048 sync min_recovery_rate 20 \
+        5 8:17 8:18 8:33 8:34 8:49 8:50 8:65 8:66 8:81 8:82
 
-Performing a 'dmsetup table' should display the CTR table used to
-construct the mapping (with possible reordering of optional
-parameters).
+'dmsetup table' displays the table used to construct the mapping.
+The optional parameters are always printed in the order listed
+above with "sync" or "nosync" always output ahead of the other
+arguments, regardless of the order used when originally loading the table.
+Arguments that can be repeated are ordered by value.
 
-Performing a 'dmsetup status' will yield information on the state and
-health of the array.  The output is as follows:
+'dmsetup status' yields information on the state and health of the
+array.
+The output is as follows:
 1: <s> <l> raid \
 2:      <raid_type> <#devices> <1 health char for each dev> <resync_ratio>
 
-Line 1 is standard DM output.  Line 2 is best shown by example:
+Line 1 is the standard output produced by device-mapper.
+Line 2 is produced by the raid target, and best explained by example:
         0 1960893648 raid raid4 5 AAAAA 2/490221568
 Here we can see the RAID type is raid4, there are 5 devices - all of
 which are 'A'live, and the array is 2/490221568 complete with recovery.
+Faulty or missing devices are marked 'D'.  Devices that are out-of-sync
+are marked 'a'.
index 7190c99d7611f8a3904fbe20f2a045abbdba51f2..5c2c02140a62dac132566aaeb4f6156b46233f41 100644 (file)
@@ -10,7 +10,7 @@ Optional properties:
 Each button (key) is represented as a sub-node of "gpio-keys":
 Subnode properties:
 
-       - gpios: OF devcie-tree gpio specificatin.
+       - gpios: OF device-tree gpio specification.
        - label: Descriptive name of the key.
        - linux,code: Keycode to emit.
 
diff --git a/Documentation/devicetree/bindings/input/fsl-mma8450.txt b/Documentation/devicetree/bindings/input/fsl-mma8450.txt
new file mode 100644 (file)
index 0000000..a00c94c
--- /dev/null
@@ -0,0 +1,11 @@
+* Freescale MMA8450 3-Axis Accelerometer
+
+Required properties:
+- compatible : "fsl,mma8450".
+
+Example:
+
+accelerometer: mma8450@1c {
+       compatible = "fsl,mma8450";
+       reg = <0x1c>;
+};
index 5a0cb1ef6164d5c363f6f4b8de0007e7820ea5ef..94b7e0f96b38fa8086ea14f7cd88718b45e84d70 100644 (file)
@@ -10,87 +10,181 @@ NOTE: For DMA Engine usage in async_tx please see:
 Below is a guide to device driver writers on how to use the Slave-DMA API of the
 DMA Engine. This is applicable only for slave DMA usage only.
 
-The slave DMA usage consists of following steps
+The slave DMA usage consists of following steps:
 1. Allocate a DMA slave channel
 2. Set slave and controller specific parameters
 3. Get a descriptor for transaction
-4. Submit the transaction and wait for callback notification
+4. Submit the transaction
+5. Issue pending requests and wait for callback notification
 
 1. Allocate a DMA slave channel
-Channel allocation is slightly different in the slave DMA context, client
-drivers typically need a channel from a particular DMA controller only and even
-in some cases a specific channel is desired. To request a channel
-dma_request_channel() API is used.
-
-Interface:
-struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
-               dma_filter_fn filter_fn,
-               void *filter_param);
-where dma_filter_fn is defined as:
-typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
-
-When the optional 'filter_fn' parameter is set to NULL dma_request_channel
-simply returns the first channel that satisfies the capability mask.  Otherwise,
-when the mask parameter is insufficient for specifying the necessary channel,
-the filter_fn routine can be used to disposition the available channels in the
-system. The filter_fn routine is called once for each free channel in the
-system.  Upon seeing a suitable channel filter_fn returns DMA_ACK which flags
-that channel to be the return value from dma_request_channel.  A channel
-allocated via this interface is exclusive to the caller, until
-dma_release_channel() is called.
+
+   Channel allocation is slightly different in the slave DMA context,
+   client drivers typically need a channel from a particular DMA
+   controller only and even in some cases a specific channel is desired.
+   To request a channel dma_request_channel() API is used.
+
+   Interface:
+       struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
+                       dma_filter_fn filter_fn,
+                       void *filter_param);
+   where dma_filter_fn is defined as:
+       typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
+
+   The 'filter_fn' parameter is optional, but highly recommended for
+   slave and cyclic channels as they typically need to obtain a specific
+   DMA channel.
+
+   When the optional 'filter_fn' parameter is NULL, dma_request_channel()
+   simply returns the first channel that satisfies the capability mask.
+
+   Otherwise, the 'filter_fn' routine will be called once for each free
+   channel which has a capability in 'mask'.  'filter_fn' is expected to
+   return 'true' when the desired DMA channel is found.
+
+   A channel allocated via this interface is exclusive to the caller,
+   until dma_release_channel() is called.
 
 2. Set slave and controller specific parameters
-Next step is always to pass some specific information to the DMA driver. Most of
-the generic information which a slave DMA can use is in struct dma_slave_config.
-It allows the clients to specify DMA direction, DMA addresses, bus widths, DMA
-burst lengths etc. If some DMA controllers have more parameters to be sent then
-they should try to embed struct dma_slave_config in their controller specific
-structure. That gives flexibility to client to pass more parameters, if
-required.
-
-Interface:
-int dmaengine_slave_config(struct dma_chan *chan,
-                                         struct dma_slave_config *config)
+
+   Next step is always to pass some specific information to the DMA
+   driver.  Most of the generic information which a slave DMA can use
+   is in struct dma_slave_config.  This allows the clients to specify
+   DMA direction, DMA addresses, bus widths, DMA burst lengths etc
+   for the peripheral.
+
+   If some DMA controllers have more parameters to be sent then they
+   should try to embed struct dma_slave_config in their controller
+   specific structure. That gives flexibility to client to pass more
+   parameters, if required.
+
+   Interface:
+       int dmaengine_slave_config(struct dma_chan *chan,
+                                 struct dma_slave_config *config)
+
+   Please see the dma_slave_config structure definition in dmaengine.h
+   for a detailed explaination of the struct members.  Please note
+   that the 'direction' member will be going away as it duplicates the
+   direction given in the prepare call.
 
 3. Get a descriptor for transaction
-For slave usage the various modes of slave transfers supported by the
-DMA-engine are:
-slave_sg       - DMA a list of scatter gather buffers from/to a peripheral
-dma_cyclic     - Perform a cyclic DMA operation from/to a peripheral till the
+
+   For slave usage the various modes of slave transfers supported by the
+   DMA-engine are:
+
+   slave_sg    - DMA a list of scatter gather buffers from/to a peripheral
+   dma_cyclic  - Perform a cyclic DMA operation from/to a peripheral till the
                  operation is explicitly stopped.
-The non NULL return of this transfer API represents a "descriptor" for the given
-transaction.
-
-Interface:
-struct dma_async_tx_descriptor *(*chan->device->device_prep_dma_sg)(
-               struct dma_chan *chan,
-               struct scatterlist *dst_sg, unsigned int dst_nents,
-               struct scatterlist *src_sg, unsigned int src_nents,
+
+   A non-NULL return of this transfer API represents a "descriptor" for
+   the given transaction.
+
+   Interface:
+       struct dma_async_tx_descriptor *(*chan->device->device_prep_slave_sg)(
+               struct dma_chan *chan, struct scatterlist *sgl,
+               unsigned int sg_len, enum dma_data_direction direction,
                unsigned long flags);
-struct dma_async_tx_descriptor *(*chan->device->device_prep_dma_cyclic)(
+
+       struct dma_async_tx_descriptor *(*chan->device->device_prep_dma_cyclic)(
                struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
                size_t period_len, enum dma_data_direction direction);
 
-4. Submit the transaction and wait for callback notification
-To schedule the transaction to be scheduled by dma device, the "descriptor"
-returned in above (3) needs to be submitted.
-To tell the dma driver that a transaction is ready to be serviced, the
-descriptor->submit() callback needs to be invoked. This chains the descriptor to
-the pending queue.
-The transactions in the pending queue can be activated by calling the
-issue_pending API. If channel is idle then the first transaction in queue is
-started and subsequent ones queued up.
-On completion of the DMA operation the next in queue is submitted and a tasklet
-triggered. The tasklet would then call the client driver completion callback
-routine for notification, if set.
-Interface:
-void dma_async_issue_pending(struct dma_chan *chan);
-
-==============================================================================
-
-Additional usage notes for dma driver writers
-1/ Although DMA engine specifies that completion callback routines cannot submit
-any new operations, but typically for slave DMA subsequent transaction may not
-be available for submit prior to callback routine being called. This requirement
-is not a requirement for DMA-slave devices. But they should take care to drop
-the spin-lock they might be holding before calling the callback routine
+   The peripheral driver is expected to have mapped the scatterlist for
+   the DMA operation prior to calling device_prep_slave_sg, and must
+   keep the scatterlist mapped until the DMA operation has completed.
+   The scatterlist must be mapped using the DMA struct device.  So,
+   normal setup should look like this:
+
+       nr_sg = dma_map_sg(chan->device->dev, sgl, sg_len);
+       if (nr_sg == 0)
+               /* error */
+
+       desc = chan->device->device_prep_slave_sg(chan, sgl, nr_sg,
+                       direction, flags);
+
+   Once a descriptor has been obtained, the callback information can be
+   added and the descriptor must then be submitted.  Some DMA engine
+   drivers may hold a spinlock between a successful preparation and
+   submission so it is important that these two operations are closely
+   paired.
+
+   Note:
+       Although the async_tx API specifies that completion callback
+       routines cannot submit any new operations, this is not the
+       case for slave/cyclic DMA.
+
+       For slave DMA, the subsequent transaction may not be available
+       for submission prior to callback function being invoked, so
+       slave DMA callbacks are permitted to prepare and submit a new
+       transaction.
+
+       For cyclic DMA, a callback function may wish to terminate the
+       DMA via dmaengine_terminate_all().
+
+       Therefore, it is important that DMA engine drivers drop any
+       locks before calling the callback function which may cause a
+       deadlock.
+
+       Note that callbacks will always be invoked from the DMA
+       engines tasklet, never from interrupt context.
+
+4. Submit the transaction
+
+   Once the descriptor has been prepared and the callback information
+   added, it must be placed on the DMA engine drivers pending queue.
+
+   Interface:
+       dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc)
+
+   This returns a cookie can be used to check the progress of DMA engine
+   activity via other DMA engine calls not covered in this document.
+
+   dmaengine_submit() will not start the DMA operation, it merely adds
+   it to the pending queue.  For this, see step 5, dma_async_issue_pending.
+
+5. Issue pending DMA requests and wait for callback notification
+
+   The transactions in the pending queue can be activated by calling the
+   issue_pending API. If channel is idle then the first transaction in
+   queue is started and subsequent ones queued up.
+
+   On completion of each DMA operation, the next in queue is started and
+   a tasklet triggered. The tasklet will then call the client driver
+   completion callback routine for notification, if set.
+
+   Interface:
+       void dma_async_issue_pending(struct dma_chan *chan);
+
+Further APIs:
+
+1. int dmaengine_terminate_all(struct dma_chan *chan)
+
+   This causes all activity for the DMA channel to be stopped, and may
+   discard data in the DMA FIFO which hasn't been fully transferred.
+   No callback functions will be called for any incomplete transfers.
+
+2. int dmaengine_pause(struct dma_chan *chan)
+
+   This pauses activity on the DMA channel without data loss.
+
+3. int dmaengine_resume(struct dma_chan *chan)
+
+   Resume a previously paused DMA channel.  It is invalid to resume a
+   channel which is not currently paused.
+
+4. enum dma_status dma_async_is_tx_complete(struct dma_chan *chan,
+        dma_cookie_t cookie, dma_cookie_t *last, dma_cookie_t *used)
+
+   This can be used to check the status of the channel.  Please see
+   the documentation in include/linux/dmaengine.h for a more complete
+   description of this API.
+
+   This can be used in conjunction with dma_async_is_complete() and
+   the cookie returned from 'descriptor->submit()' to check for
+   completion of a specific DMA transaction.
+
+   Note:
+       Not all DMA engine drivers can return reliable information for
+       a running DMA channel.  It is recommended that DMA engine users
+       pause or stop (via dmaengine_terminate_all) the channel before
+       using this API.
index 7be15e44d48166246cd85c99e8219b08e0fce52c..82a5d250d75e705f92b8c32ff634ca1b945ef008 100644 (file)
@@ -143,8 +143,7 @@ o provide a way to configure fault attributes
   failslab, fail_page_alloc, and fail_make_request use this way.
   Helper functions:
 
-       init_fault_attr_dentries(entries, attr, name);
-       void cleanup_fault_attr_dentries(entries);
+       fault_create_debugfs_attr(name, parent, attr);
 
 - module parameters
 
index ea0bace0124ad7db95b43f3e8f30c872584bc66b..c4a6e148732ae549be7e25b8863912a2f924d254 100644 (file)
@@ -296,15 +296,6 @@ Who:       Ravikiran Thirumalai <kiran@scalex86.org>
 
 ---------------------------
 
-What:  CONFIG_THERMAL_HWMON
-When:  January 2009
-Why:   This option was introduced just to allow older lm-sensors userspace
-       to keep working over the upgrade to 2.6.26. At the scheduled time of
-       removal fixed lm-sensors (2.x or 3.x) should be readily available.
-Who:   Rene Herman <rene.herman@gmail.com>
-
----------------------------
-
 What:  Code that is now under CONFIG_WIRELESS_EXT_SYSFS
        (in net/core/net-sysfs.c)
 When:  After the only user (hal) has seen a release with the patches
@@ -590,3 +581,14 @@ Why:       This driver has been superseded by g_mass_storage.
 Who:   Alan Stern <stern@rowland.harvard.edu>
 
 ----------------------------
+
+What:   threeg and interface sysfs files in /sys/devices/platform/acer-wmi
+When:   2012
+Why:    In 3.0, we can now autodetect internal 3G device and already have
+       the threeg rfkill device. So, we plan to remove threeg sysfs support
+       for it's no longer necessary.
+
+       We also plan to remove interface sysfs file that exposed which ACPI-WMI
+       interface that was used by acer-wmi driver. It will replaced by
+       information log when acer-wmi initial.
+Who:    Lee, Chun-Yi <jlee@novell.com>
index ace200b7c2140145c457ee86ce7f02b532c85e87..37c4d84a0e570240222ea08d95b0fb79280c29f3 100644 (file)
@@ -106,13 +106,20 @@ separated by spaces:
       To use the first on-chip serial port at baud rate 115200, no parity, 8
       bits, and no flow control.
 
-  (*) root=/dev/<xxxx>
+  (*) root=<xxxx>
 
-      This specifies the device upon which the root filesystem resides. For
-      example:
+      This specifies the device upon which the root filesystem resides. It
+      may be specified by major and minor number, device path, or even
+      partition uuid, if supported.  For example:
 
        /dev/nfs        NFS root filesystem
        /dev/mtdblock3  Fourth RedBoot partition on the System Flash
+       PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF/PARTNROFF=1
+               first partition after the partition with the given UUID
+       253:0           Device with major 253 and minor 0
+
+      Authoritative information can be found in
+      "Documentation/kernel-parameters.txt".
 
   (*) rw
 
index 72ba8d51dbc15f10b9ff1089b92c7114e75303d3..845a191004b1fd20373b6b3738f8404b0d85c2c4 100644 (file)
@@ -292,6 +292,7 @@ Code  Seq#(hex)     Include File            Comments
                                        <mailto:buk@buks.ipn.de>
 0xA0   all     linux/sdp/sdp.h         Industrial Device Project
                                        <mailto:kenji@bitgate.com>
+0xA2   00-0F   arch/tile/include/asm/hardwall.h
 0xA3   80-8F   Port ACL                in development:
                                        <mailto:tlewis@mindspring.com>
 0xA3   90-9F   linux/dtlk.h
index 4ca93898fbd3d984a3bccf550b5281d3f0a90e56..e279b724291239677041f3d4a3f002e566db8ae6 100644 (file)
@@ -163,6 +163,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 
                        See also Documentation/power/pm.txt, pci=noacpi
 
+       acpi_rsdp=      [ACPI,EFI,KEXEC]
+                       Pass the RSDP address to the kernel, mostly used
+                       on machines running EFI runtime service to boot the
+                       second kernel for kdump.
+
        acpi_apic_instance=     [ACPI, IOAPIC]
                        Format: <int>
                        2: use 2nd APIC table, if available
@@ -546,6 +551,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        /proc/<pid>/coredump_filter.
                        See also Documentation/filesystems/proc.txt.
 
+       cpuidle.off=1   [CPU_IDLE]
+                       disable the cpuidle sub-system
+
        cpcihp_generic= [HW,PCI] Generic port I/O CompactPCI driver
                        Format:
                        <first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]
@@ -2153,6 +2161,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        [HW,MOUSE] Controls Logitech smartscroll autorepeat.
                        0 = disabled, 1 = enabled (default).
 
+       pstore.backend= Specify the name of the pstore backend to use
+
        pt.             [PARIDE]
                        See Documentation/blockdev/paride.txt.
 
@@ -2238,6 +2248,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
        ro              [KNL] Mount root device read-only on boot
 
        root=           [KNL] Root filesystem
+                       See name_to_dev_t comment in init/do_mounts.c.
 
        rootdelay=      [KNL] Delay (in seconds) to pause before attempting to
                        mount the root filesystem
index c93bed66e25d459d36bcd8a0d1308a1fac17c6c1..97d45f276fe6e235801b8e1584c9609574d9cc4e 100644 (file)
@@ -129,6 +129,20 @@ decimal 11 is the major of SCSI CD-ROMs, and the minor 0 stands for
 the first of these. You can find out all valid major numbers by
 looking into include/linux/major.h.
 
+In addition to major and minor numbers, if the device containing your
+root partition uses a partition table format with unique partition
+identifiers, then you may use them.  For instance,
+"root=PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF".  It is also
+possible to reference another partition on the same device using a
+known partition UUID as the starting point.  For example,
+if partition 5 of the device has the UUID of
+00112233-4455-6677-8899-AABBCCDDEEFF then partition 3 may be found as
+follows:
+  PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF/PARTNROFF=-2
+
+Authoritative information can be found in
+"Documentation/kernel-parameters.txt".
+
 
 2.2) ro, rw
 -----------
index 675612ff41ae2b902d89ad93e6404515f2bdef19..5dd960d751743ba8a49b7e28c69628cf00807930 100644 (file)
@@ -599,7 +599,7 @@ num_unsol_na
        affect only the active-backup mode.  These options were added for
        bonding versions 3.3.0 and 3.4.0 respectively.
 
-       From Linux 2.6.40 and bonding version 3.7.1, these notifications
+       From Linux 3.0 and bonding version 3.7.1, these notifications
        are generated by the ipv4 and ipv6 code and the numbers of
        repetitions cannot be set independently.
 
index 14dd3c6ad97ee852d0273a7a66c57b5ebe74b34b..4ce5450ab6e833cef372ef2755993353cae50935 100644 (file)
@@ -54,11 +54,10 @@ referred to as subsystem-level callbacks in what follows.
 By default, the callbacks are always invoked in process context with interrupts
 enabled.  However, subsystems can use the pm_runtime_irq_safe() helper function
 to tell the PM core that a device's ->runtime_suspend() and ->runtime_resume()
-callbacks should be invoked in atomic context with interrupts disabled
-(->runtime_idle() is still invoked the default way).  This implies that these
-callback routines must not block or sleep, but it also means that the
-synchronous helper functions listed at the end of Section 4 can be used within
-an interrupt handler or in an atomic context.
+callbacks should be invoked in atomic context with interrupts disabled.
+This implies that these callback routines must not block or sleep, but it also
+means that the synchronous helper functions listed at the end of Section 4 can
+be used within an interrupt handler or in an atomic context.
 
 The subsystem-level suspend callback is _entirely_ _responsible_ for handling
 the suspend of the device as appropriate, which may, but need not include
@@ -483,6 +482,7 @@ pm_runtime_suspend()
 pm_runtime_autosuspend()
 pm_runtime_resume()
 pm_runtime_get_sync()
+pm_runtime_put_sync()
 pm_runtime_put_sync_suspend()
 
 5. Runtime PM Initialization, Device Probing and Removal
index c9c6324a7a9f887e930b6fabfdb7039af3a02dae..51d42fbc8dc40a9f09da5471085a928c244e62ed 100644 (file)
@@ -2643,9 +2643,8 @@ S:        Maintained
 F:     arch/x86/math-emu/
 
 FRAME RELAY DLCI/FRAD (Sangoma drivers too)
-M:     Mike McLagan <mike.mclagan@linux.org>
 L:     netdev@vger.kernel.org
-S:     Maintained
+S:     Orphan
 F:     drivers/net/wan/dlci.c
 F:     drivers/net/wan/sdla.c
 
@@ -3367,6 +3366,12 @@ F:       drivers/net/ixgb/
 F:     drivers/net/ixgbe/
 F:     drivers/net/ixgbevf/
 
+INTEL MRST PMU DRIVER
+M:     Len Brown <len.brown@intel.com>
+L:     linux-pm@lists.linux-foundation.org
+S:     Supported
+F:     arch/x86/platform/mrst/pmu.*
+
 INTEL PRO/WIRELESS 2100 NETWORK CONNECTION SUPPORT
 L:     linux-wireless@vger.kernel.org
 S:     Orphan
@@ -4409,10 +4414,10 @@ F:      net/*/netfilter/
 F:     net/netfilter/
 
 NETLABEL
-M:     Paul Moore <paul.moore@hp.com>
+M:     Paul Moore <paul@paul-moore.com>
 W:     http://netlabel.sf.net
 L:     netdev@vger.kernel.org
-S:     Supported
+S:     Maintained
 F:     Documentation/netlabel/
 F:     include/net/netlabel.h
 F:     net/netlabel/
@@ -4457,7 +4462,6 @@ F:        include/linux/netdevice.h
 NETWORKING [IPv4/IPv6]
 M:     "David S. Miller" <davem@davemloft.net>
 M:     Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
-M:     "Pekka Savola (ipv6)" <pekkas@netcore.fi>
 M:     James Morris <jmorris@namei.org>
 M:     Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
 M:     Patrick McHardy <kaber@trash.net>
@@ -4470,7 +4474,7 @@ F:        include/net/ip*
 F:     arch/x86/net/*
 
 NETWORKING [LABELED] (NetLabel, CIPSO, Labeled IPsec, SECMARK)
-M:     Paul Moore <paul.moore@hp.com>
+M:     Paul Moore <paul@paul-moore.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 
@@ -4722,6 +4726,7 @@ S:        Maintained
 F:     drivers/of
 F:     include/linux/of*.h
 K:     of_get_property
+K:     of_match_table
 
 OPENRISC ARCHITECTURE
 M:     Jonas Bonn <jonas@southpole.se>
@@ -6318,6 +6323,7 @@ F:        include/linux/sysv_fs.h
 TARGET SUBSYSTEM
 M:     Nicholas A. Bellinger <nab@linux-iscsi.org>
 L:     linux-scsi@vger.kernel.org
+L:     target-devel@vger.kernel.org
 L:     http://groups.google.com/group/linux-iscsi-target-dev
 W:     http://www.linux-iscsi.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/nab/lio-core-2.6.git master
index 26b0e2397a5724140bc8683fedd6bbae67706fe5..4b0669cbb3b01d4c0eb66f364859cb6f65654693 100644 (file)
@@ -178,4 +178,7 @@ config HAVE_ARCH_MUTEX_CPU_RELAX
 config HAVE_RCU_TABLE_FREE
        bool
 
+config ARCH_HAVE_NMI_SAFE_CMPXCHG
+       bool
+
 source "kernel/gcov/Kconfig"
index ca2da8da6e9c68f057d0a393de2c0018c32c9d1d..60cde53d266c38da1848b01538698bc85a309453 100644 (file)
@@ -14,6 +14,7 @@ config ALPHA
        select AUTO_IRQ_AFFINITY if SMP
        select GENERIC_IRQ_SHOW
        select ARCH_WANT_OPTIONAL_GPIOLIB
+       select ARCH_HAVE_NMI_SAFE_CMPXCHG
        help
          The Alpha is a 64-bit general-purpose processor designed and
          marketed by the Digital Equipment Corporation of blessed memory,
index 5e1e541972277f38a123d7964fca081629584d50..1a347f481e5e7a5c022b7795c6b9542cb77d529e 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/uaccess.h>
 #include <linux/random.h>
 #include <linux/hw_breakpoint.h>
+#include <linux/cpuidle.h>
 
 #include <asm/cacheflush.h>
 #include <asm/leds.h>
@@ -196,7 +197,8 @@ void cpu_idle(void)
                                cpu_relax();
                        } else {
                                stop_critical_timings();
-                               pm_idle();
+                               if (cpuidle_idle_call())
+                                       pm_idle();
                                start_critical_timings();
                                /*
                                 * This will eventually be removed - pm_idle
index e9d689b7c833d2c5218ce3d97876c3023785f4e8..197e96f7040594fa1994f6d7f61edddff6ccd65d 100644 (file)
@@ -10,6 +10,7 @@ config AVR32
        select GENERIC_IRQ_PROBE
        select HARDIRQS_SW_RESEND
        select GENERIC_IRQ_SHOW
+       select ARCH_HAVE_NMI_SAFE_CMPXCHG
        help
          AVR32 is a high-performance 32-bit RISC microprocessor core,
          designed for cost-sensitive embedded applications, with particular
index 8502653736113b0c51cba3c529ec20285ea4203e..466af40c5822cfb93a8d0a14e007b513c8cc6e6c 100644 (file)
@@ -158,7 +158,7 @@ static int sync_serial_open(struct inode *inode, struct file *file);
 static int sync_serial_release(struct inode *inode, struct file *file);
 static unsigned int sync_serial_poll(struct file *filp, poll_table *wait);
 
-static int sync_serial_ioctl(struct file *file,
+static long sync_serial_ioctl(struct file *file,
        unsigned int cmd, unsigned long arg);
 static ssize_t sync_serial_write(struct file *file, const char *buf,
        size_t count, loff_t *ppos);
@@ -625,11 +625,11 @@ static int sync_serial_open(struct inode *inode, struct file *file)
                        *R_IRQ_MASK1_SET = 1 << port->data_avail_bit;
                DEBUG(printk(KERN_DEBUG "sser%d rec started\n", dev));
        }
-       ret = 0;
+       err = 0;
        
 out:
        mutex_unlock(&sync_serial_mutex);
-       return ret;
+       return err;
 }
 
 static int sync_serial_release(struct inode *inode, struct file *file)
index 907cfb5a873dae89b4b041b3f17a4acda7dc8680..ba0e5965d6e3ed2f901e48cf1c5ee2d52435d960 100644 (file)
@@ -20,6 +20,9 @@
 #define crisv10_mask_irq(irq_nr) (*R_VECT_MASK_CLR = 1 << (irq_nr));
 #define crisv10_unmask_irq(irq_nr) (*R_VECT_MASK_SET = 1 << (irq_nr));
 
+extern void kgdb_init(void);
+extern void breakpoint(void);
+
 /* don't use set_int_vector, it bypasses the linux interrupt handlers. it is
  * global just so that the kernel gdb can use it.
  */
index 29b74a10583039b474e3fd941665af8f88fd41e9..332f19c54557aa09b8bdf9788f1c6998cdf93423 100644 (file)
@@ -11,8 +11,6 @@
 
 #ifdef __KERNEL__
 
-#define __HAVE_ARCH_THREAD_INFO_ALLOCATOR
-
 #ifndef __ASSEMBLY__
 #include <asm/types.h>
 #include <asm/processor.h>
@@ -67,8 +65,10 @@ struct thread_info {
 
 #define init_thread_info       (init_thread_union.thread_info)
 
+#define __HAVE_ARCH_THREAD_INFO_ALLOCATOR
 /* thread information allocation */
-#define alloc_thread_info(tsk, node) ((struct thread_info *) __get_free_pages(GFP_KERNEL,1))
+#define alloc_thread_info_node(tsk, node)      \
+       ((struct thread_info *) __get_free_pages(GFP_KERNEL, 1))
 #define free_thread_info(ti) free_pages((unsigned long) (ti), 1)
 
 #endif /* !__ASSEMBLY__ */
index cb884e48942560f23eb9de40c24cb62d9b7eb1e9..bad27a6ff407bfcce2aecbac934278eabe9c194f 100644 (file)
@@ -7,6 +7,7 @@ config FRV
        select HAVE_PERF_EVENTS
        select HAVE_GENERIC_HARDIRQS
        select GENERIC_IRQ_SHOW
+       select ARCH_HAVE_NMI_SAFE_CMPXCHG
 
 config ZONE_DMA
        bool
index 137b277f7e56b073a4ef2cbc4fef4429e28053b1..12485471495893cd00c80be024b7983c11e8168c 100644 (file)
@@ -27,6 +27,8 @@ config IA64
        select GENERIC_PENDING_IRQ if SMP
        select IRQ_PER_CPU
        select GENERIC_IRQ_SHOW
+       select ARCH_WANT_OPTIONAL_GPIOLIB
+       select ARCH_HAVE_NMI_SAFE_CMPXCHG
        default y
        help
          The Itanium Processor Family is Intel's 64-bit successor to
@@ -89,6 +91,9 @@ config GENERIC_TIME_VSYSCALL
 config HAVE_SETUP_PER_CPU_AREA
        def_bool y
 
+config GENERIC_GPIO
+       def_bool y
+
 config DMI
        bool
        default y
diff --git a/arch/ia64/include/asm/gpio.h b/arch/ia64/include/asm/gpio.h
new file mode 100644 (file)
index 0000000..590a20d
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Generic GPIO API implementation for IA-64.
+ *
+ * A stright copy of that for PowerPC which was:
+ *
+ * Copyright (c) 2007-2008  MontaVista Software, Inc.
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _ASM_IA64_GPIO_H
+#define _ASM_IA64_GPIO_H
+
+#include <linux/errno.h>
+#include <asm-generic/gpio.h>
+
+#ifdef CONFIG_GPIOLIB
+
+/*
+ * We don't (yet) implement inlined/rapid versions for on-chip gpios.
+ * Just call gpiolib.
+ */
+static inline int gpio_get_value(unsigned int gpio)
+{
+       return __gpio_get_value(gpio);
+}
+
+static inline void gpio_set_value(unsigned int gpio, int value)
+{
+       __gpio_set_value(gpio, value);
+}
+
+static inline int gpio_cansleep(unsigned int gpio)
+{
+       return __gpio_cansleep(gpio);
+}
+
+static inline int gpio_to_irq(unsigned int gpio)
+{
+       return __gpio_to_irq(gpio);
+}
+
+static inline int irq_to_gpio(unsigned int irq)
+{
+       return -EINVAL;
+}
+
+#endif /* CONFIG_GPIOLIB */
+
+#endif /* _ASM_IA64_GPIO_H */
index 6fc03aff046c6a133a79d1305aa7b163fd310b70..c38d22e5e902f9764e1963ed7f65638f3a419d10 100644 (file)
@@ -156,7 +156,7 @@ prefix##_get_next_variable (unsigned long *name_size, efi_char16_t *name,      \
 #define STUB_SET_VARIABLE(prefix, adjust_arg)                                 \
 static efi_status_t                                                           \
 prefix##_set_variable (efi_char16_t *name, efi_guid_t *vendor,                \
-                      unsigned long attr, unsigned long data_size,            \
+                      u32 attr, unsigned long data_size,                      \
                       void *data)                                             \
 {                                                                             \
        struct ia64_fpreg fr[6];                                               \
index 284cd3771eaaf7b1b0c1f3fd5a35789c116954eb..9e8ee9d2b8ca3c55ce540aee8addcbf308ea9e06 100644 (file)
@@ -6,6 +6,7 @@ config M68K
        select GENERIC_ATOMIC64 if MMU
        select HAVE_GENERIC_HARDIRQS if !MMU
        select GENERIC_IRQ_SHOW if !MMU
+       select ARCH_HAVE_NMI_SAFE_CMPXCHG if RMW_INSNS
 
 config RWSEM_GENERIC_SPINLOCK
        bool
index 65adc86a230e705d64b41b1c69a886268f9a6eb6..e077b0bf56ca51910df1775acd66f319afdae13a 100644 (file)
@@ -15,6 +15,7 @@ config PARISC
        select HAVE_GENERIC_HARDIRQS
        select GENERIC_IRQ_PROBE
        select IRQ_PER_CPU
+       select ARCH_HAVE_NMI_SAFE_CMPXCHG
 
        help
          The PA-RISC microprocessor is designed by Hewlett-Packard and used
index b1dc71f5534e17f7218406512c8b731347fc9f2d..4054b31e0fa9da81a6db43081842c02fc3079954 100644 (file)
@@ -258,10 +258,10 @@ static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
 
 #define ATOMIC64_INIT(i) ((atomic64_t) { (i) })
 
-static __inline__ int
+static __inline__ s64
 __atomic64_add_return(s64 i, atomic64_t *v)
 {
-       int ret;
+       s64 ret;
        unsigned long flags;
        _atomic_spin_lock_irqsave(v, flags);
 
index 67a33cc27ef2741c73c598ec6ddbcce26ce70d21..2388bdb3283283870016fc03448e8ac569011d57 100644 (file)
@@ -5,11 +5,14 @@
 
 #include <linux/futex.h>
 #include <linux/uaccess.h>
+#include <asm/atomic.h>
 #include <asm/errno.h>
 
 static inline int
 futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
 {
+       unsigned long int flags;
+       u32 val;
        int op = (encoded_op >> 28) & 7;
        int cmp = (encoded_op >> 24) & 15;
        int oparg = (encoded_op << 8) >> 20;
@@ -18,21 +21,58 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
        if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
                oparg = 1 << oparg;
 
-       if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32)))
+       if (!access_ok(VERIFY_WRITE, uaddr, sizeof(*uaddr)))
                return -EFAULT;
 
        pagefault_disable();
 
+       _atomic_spin_lock_irqsave(uaddr, flags);
+
        switch (op) {
        case FUTEX_OP_SET:
+               /* *(int *)UADDR2 = OPARG; */
+               ret = get_user(oldval, uaddr);
+               if (!ret)
+                       ret = put_user(oparg, uaddr);
+               break;
        case FUTEX_OP_ADD:
+               /* *(int *)UADDR2 += OPARG; */
+               ret = get_user(oldval, uaddr);
+               if (!ret) {
+                       val = oldval + oparg;
+                       ret = put_user(val, uaddr);
+               }
+               break;
        case FUTEX_OP_OR:
+               /* *(int *)UADDR2 |= OPARG; */
+               ret = get_user(oldval, uaddr);
+               if (!ret) {
+                       val = oldval | oparg;
+                       ret = put_user(val, uaddr);
+               }
+               break;
        case FUTEX_OP_ANDN:
+               /* *(int *)UADDR2 &= ~OPARG; */
+               ret = get_user(oldval, uaddr);
+               if (!ret) {
+                       val = oldval & ~oparg;
+                       ret = put_user(val, uaddr);
+               }
+               break;
        case FUTEX_OP_XOR:
+               /* *(int *)UADDR2 ^= OPARG; */
+               ret = get_user(oldval, uaddr);
+               if (!ret) {
+                       val = oldval ^ oparg;
+                       ret = put_user(val, uaddr);
+               }
+               break;
        default:
                ret = -ENOSYS;
        }
 
+       _atomic_spin_unlock_irqrestore(uaddr, flags);
+
        pagefault_enable();
 
        if (!ret) {
@@ -54,7 +94,9 @@ static inline int
 futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
                              u32 oldval, u32 newval)
 {
+       int ret;
        u32 val;
+       unsigned long flags;
 
        /* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is
         * our gateway page, and causes no end of trouble...
@@ -65,12 +107,24 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
        if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
                return -EFAULT;
 
-       if (get_user(val, uaddr))
-               return -EFAULT;
-       if (val == oldval && put_user(newval, uaddr))
-               return -EFAULT;
+       /* HPPA has no cmpxchg in hardware and therefore the
+        * best we can do here is use an array of locks. The
+        * lock selected is based on a hash of the userspace
+        * address. This should scale to a couple of CPUs.
+        */
+
+       _atomic_spin_lock_irqsave(uaddr, flags);
+
+       ret = get_user(val, uaddr);
+
+       if (!ret && val == oldval)
+               ret = put_user(newval, uaddr);
+
        *uval = val;
-       return 0;
+
+       _atomic_spin_unlock_irqrestore(uaddr, flags);
+
+       return ret;
 }
 
 #endif /*__KERNEL__*/
index 3392de3e7be08eef3e60e377fbd7fe5039fb9ac9..d61de64f990af771e6d56a41beda7473f062060f 100644 (file)
 #define __NR_open_by_handle_at (__NR_Linux + 326)
 #define __NR_syncfs            (__NR_Linux + 327)
 #define __NR_setns             (__NR_Linux + 328)
+#define __NR_sendmmsg          (__NR_Linux + 329)
 
-#define __NR_Linux_syscalls    (__NR_setns + 1)
+#define __NR_Linux_syscalls    (__NR_sendmmsg + 1)
 
 
 #define __IGNORE_select                /* newselect */
index 34a4f5a2fffbdc9cf36ea2a5ebc19a6ffe1dc9d7..e66366fd2abc8cbf55e74455c9e26f22bd89aedb 100644 (file)
        ENTRY_COMP(open_by_handle_at)
        ENTRY_SAME(syncfs)
        ENTRY_SAME(setns)
+       ENTRY_COMP(sendmmsg)
 
        /* Nothing yet */
 
index 374c475e56a3238bf5e55e161cd13d44a468d7be..6926b61acfeac74133f6fe2d3d206e06b92162f1 100644 (file)
@@ -136,6 +136,7 @@ config PPC
        select HAVE_SYSCALL_TRACEPOINTS
        select HAVE_BPF_JIT if (PPC64 && NET)
        select HAVE_ARCH_JUMP_LABEL
+       select ARCH_HAVE_NMI_SAFE_CMPXCHG
 
 config EARLY_PRINTK
        bool
index c03fef7a9c2220c45ca1584ad8f65191bb5c9e76..ed5cb5af52816940d45c6610e3e2e0d8c1d60a82 100644 (file)
@@ -81,6 +81,7 @@ config S390
        select INIT_ALL_POSSIBLE
        select HAVE_IRQ_WORK
        select HAVE_PERF_EVENTS
+       select ARCH_HAVE_NMI_SAFE_CMPXCHG
        select HAVE_KERNEL_GZIP
        select HAVE_KERNEL_BZIP2
        select HAVE_KERNEL_LZMA
@@ -273,11 +274,11 @@ config MARCH_Z10
          on older machines.
 
 config MARCH_Z196
-       bool "IBM zEnterprise 196"
+       bool "IBM zEnterprise 114 and 196"
        help
-         Select this to enable optimizations for IBM zEnterprise 196
-         (2817 series). The kernel will be slightly faster but will not work
-         on older machines.
+         Select this to enable optimizations for IBM zEnterprise 114 and 196
+         (2818 and 2817 series). The kernel will be slightly faster but will
+         not work on older machines.
 
 endchoice
 
index 5e95d95450b3734d9c771a40a54037fbbc56619b..97cc4403fabfe6a0a4ad0b21a4b9b5fa41df6e28 100644 (file)
@@ -167,5 +167,6 @@ enum diag308_rc {
 };
 
 extern int diag308(unsigned long subcode, void *addr);
+extern void diag308_reset(void);
 
 #endif /* _ASM_S390_IPL_H */
index f26280d9e88dbb35029e8c92bbef08b9a17cda98..e85c911aabf04d4660059900fba13dd75e269cb4 100644 (file)
@@ -18,6 +18,7 @@ void system_call(void);
 void pgm_check_handler(void);
 void mcck_int_handler(void);
 void io_int_handler(void);
+void psw_restart_int_handler(void);
 
 #ifdef CONFIG_32BIT
 
@@ -150,7 +151,10 @@ struct _lowcore {
         */
        __u32   ipib;                           /* 0x0e00 */
        __u32   ipib_checksum;                  /* 0x0e04 */
-       __u8    pad_0x0e08[0x0f00-0x0e08];      /* 0x0e08 */
+
+       /* 64 bit save area */
+       __u64   save_area_64;                   /* 0x0e08 */
+       __u8    pad_0x0e10[0x0f00-0x0e10];      /* 0x0e10 */
 
        /* Extended facility list */
        __u64   stfle_fac_list[32];             /* 0x0f00 */
@@ -286,7 +290,10 @@ struct _lowcore {
         */
        __u64   ipib;                           /* 0x0e00 */
        __u32   ipib_checksum;                  /* 0x0e08 */
-       __u8    pad_0x0e0c[0x0f00-0x0e0c];      /* 0x0e0c */
+
+       /* 64 bit save area */
+       __u64   save_area_64;                   /* 0x0e0c */
+       __u8    pad_0x0e14[0x0f00-0x0e14];      /* 0x0e14 */
 
        /* Extended facility list */
        __u64   stfle_fac_list[32];             /* 0x0f00 */
index 55dfcc8bdc0d5a13e18dd49a92ae780feed7b068..a4b6229e5d4b703f9cf6da874e13f7dc1cc468fb 100644 (file)
@@ -119,14 +119,12 @@ struct stack_frame {
  * Do necessary setup to start up a new thread.
  */
 #define start_thread(regs, new_psw, new_stackp) do {           \
-       set_fs(USER_DS);                                        \
        regs->psw.mask  = psw_user_bits;                        \
        regs->psw.addr  = new_psw | PSW_ADDR_AMODE;             \
        regs->gprs[15]  = new_stackp;                           \
 } while (0)
 
 #define start_thread31(regs, new_psw, new_stackp) do {         \
-       set_fs(USER_DS);                                        \
        regs->psw.mask  = psw_user32_bits;                      \
        regs->psw.addr  = new_psw | PSW_ADDR_AMODE;             \
        regs->gprs[15]  = new_stackp;                           \
index d382629a01728de29a1ceb396a637c26ed4f94c1..6582f69f23899b4989c05b9aacf0fb6de4bed4c2 100644 (file)
@@ -113,6 +113,7 @@ extern void pfault_fini(void);
 
 extern void cmma_init(void);
 extern int memcpy_real(void *, void *, size_t);
+extern void copy_to_absolute_zero(void *dest, void *src, size_t count);
 
 #define finish_arch_switch(prev) do {                                       \
        set_fs(current->thread.mm_segment);                                  \
index 05d8f38734ec7cbabc27219c7dc9e6dcf574e3c9..532fd43221565a2fa27e4b39af91856af203b82b 100644 (file)
@@ -27,12 +27,9 @@ int main(void)
        BLANK();
        DEFINE(__TASK_pid, offsetof(struct task_struct, pid));
        BLANK();
-       DEFINE(__THREAD_per_cause,
-              offsetof(struct task_struct, thread.per_event.cause));
-       DEFINE(__THREAD_per_address,
-              offsetof(struct task_struct, thread.per_event.address));
-       DEFINE(__THREAD_per_paid,
-              offsetof(struct task_struct, thread.per_event.paid));
+       DEFINE(__THREAD_per_cause, offsetof(struct task_struct, thread.per_event.cause));
+       DEFINE(__THREAD_per_address, offsetof(struct task_struct, thread.per_event.address));
+       DEFINE(__THREAD_per_paid, offsetof(struct task_struct, thread.per_event.paid));
        BLANK();
        DEFINE(__TI_task, offsetof(struct thread_info, task));
        DEFINE(__TI_domain, offsetof(struct thread_info, exec_domain));
@@ -142,6 +139,7 @@ int main(void)
        DEFINE(__LC_FPREGS_SAVE_AREA, offsetof(struct _lowcore, floating_pt_save_area));
        DEFINE(__LC_GPREGS_SAVE_AREA, offsetof(struct _lowcore, gpregs_save_area));
        DEFINE(__LC_CREGS_SAVE_AREA, offsetof(struct _lowcore, cregs_save_area));
+       DEFINE(__LC_SAVE_AREA_64, offsetof(struct _lowcore, save_area_64));
 #ifdef CONFIG_32BIT
        DEFINE(SAVE_AREA_BASE, offsetof(struct _lowcore, extended_save_area_addr));
 #else /* CONFIG_32BIT */
index 209938c1dfc84c06ff94b2ddc6037d9738f6b457..255435663bf820299734afdce573fa52050e9d87 100644 (file)
@@ -76,6 +76,42 @@ s390_base_pgm_handler_fn:
        .quad   0
        .previous
 
+#
+# Calls diag 308 subcode 1 and continues execution
+#
+# The following conditions must be ensured before calling this function:
+# * Prefix register = 0
+# * Lowcore protection is disabled
+#
+ENTRY(diag308_reset)
+       larl    %r4,.Lctlregs           # Save control registers
+       stctg   %c0,%c15,0(%r4)
+       larl    %r4,.Lrestart_psw       # Setup restart PSW at absolute 0
+       lghi    %r3,0
+       lg      %r4,0(%r4)              # Save PSW
+       sturg   %r4,%r3                 # Use sturg, because of large pages
+       lghi    %r1,1
+       diag    %r1,%r1,0x308
+.Lrestart_part2:
+       lhi     %r0,0                   # Load r0 with zero
+       lhi     %r1,2                   # Use mode 2 = ESAME (dump)
+       sigp    %r1,%r0,0x12            # Switch to ESAME mode
+       sam64                           # Switch to 64 bit addressing mode
+       larl    %r4,.Lctlregs           # Restore control registers
+       lctlg   %c0,%c15,0(%r4)
+       br      %r14
+.align 16
+.Lrestart_psw:
+       .long   0x00080000,0x80000000 + .Lrestart_part2
+
+       .section .bss
+.align 8
+.Lctlregs:
+       .rept   16
+       .quad   0
+       .endr
+       .previous
+
 #else /* CONFIG_64BIT */
 
 ENTRY(s390_base_mcck_handler)
index eee999853a7cbaa196c2d3b740591e457847ea92..a9a285b8c4ad3ccdfafcaf2b1412de4900e2b219 100644 (file)
@@ -380,20 +380,13 @@ asmlinkage long sys32_sigreturn(void)
                goto badframe;
        if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32))
                goto badframe;
-
        sigdelsetmask(&set, ~_BLOCKABLE);
-       spin_lock_irq(&current->sighand->siglock);
-       current->blocked = set;
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
-
+       set_current_blocked(&set);
        if (restore_sigregs32(regs, &frame->sregs))
                goto badframe;
        if (restore_sigregs_gprs_high(regs, frame->gprs_high))
                goto badframe;
-
        return regs->gprs[2];
-
 badframe:
        force_sig(SIGSEGV, current);
        return 0;
@@ -413,31 +406,22 @@ asmlinkage long sys32_rt_sigreturn(void)
                goto badframe;
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
-
        sigdelsetmask(&set, ~_BLOCKABLE);
-       spin_lock_irq(&current->sighand->siglock);
-       current->blocked = set;
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
-
+       set_current_blocked(&set);
        if (restore_sigregs32(regs, &frame->uc.uc_mcontext))
                goto badframe;
        if (restore_sigregs_gprs_high(regs, frame->gprs_high))
                goto badframe;
-
        err = __get_user(ss_sp, &frame->uc.uc_stack.ss_sp);
        st.ss_sp = compat_ptr(ss_sp);
        err |= __get_user(st.ss_size, &frame->uc.uc_stack.ss_size);
        err |= __get_user(st.ss_flags, &frame->uc.uc_stack.ss_flags);
        if (err)
                goto badframe; 
-
        set_fs (KERNEL_DS);
        do_sigaltstack((stack_t __force __user *)&st, NULL, regs->gprs[15]);
        set_fs (old_fs);
-
        return regs->gprs[2];
-
 badframe:
        force_sig(SIGSEGV, current);
        return 0;
@@ -605,10 +589,10 @@ give_sigsegv:
  * OK, we're invoking a handler
  */    
 
-int
-handle_signal32(unsigned long sig, struct k_sigaction *ka,
-               siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
+int handle_signal32(unsigned long sig, struct k_sigaction *ka,
+                   siginfo_t *info, sigset_t *oldset, struct pt_regs *regs)
 {
+       sigset_t blocked;
        int ret;
 
        /* Set up the stack frame */
@@ -616,15 +600,12 @@ handle_signal32(unsigned long sig, struct k_sigaction *ka,
                ret = setup_rt_frame32(sig, ka, info, oldset, regs);
        else
                ret = setup_frame32(sig, ka, oldset, regs);
-
-       if (ret == 0) {
-               spin_lock_irq(&current->sighand->siglock);
-               sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
-               if (!(ka->sa.sa_flags & SA_NODEFER))
-                       sigaddset(&current->blocked,sig);
-               recalc_sigpending();
-               spin_unlock_irq(&current->sighand->siglock);
-       }
-       return ret;
+       if (ret)
+               return ret;
+       sigorsets(&blocked, &current->blocked, &ka->sa.sa_mask);
+       if (!(ka->sa.sa_flags & SA_NODEFER))
+               sigaddset(&blocked, sig);
+       set_current_blocked(&blocked);
+       return 0;
 }
 
index 3eab7cfab07c97d962e68f8ab37d9076b0305f88..02ec8fe7d03fe575c3969e14b750b3a6bf81c0b6 100644 (file)
@@ -849,6 +849,34 @@ restart_crash:
 restart_go:
 #endif
 
+#
+# PSW restart interrupt handler
+#
+ENTRY(psw_restart_int_handler)
+       st      %r15,__LC_SAVE_AREA_64(%r0)     # save r15
+       basr    %r15,0
+0:     l       %r15,.Lrestart_stack-0b(%r15)   # load restart stack
+       l       %r15,0(%r15)
+       ahi     %r15,-SP_SIZE                   # make room for pt_regs
+       stm     %r0,%r14,SP_R0(%r15)            # store gprs %r0-%r14 to stack
+       mvc     SP_R15(4,%r15),__LC_SAVE_AREA_64(%r0)# store saved %r15 to stack
+       mvc     SP_PSW(8,%r15),__LC_RST_OLD_PSW(%r0) # store restart old psw
+       xc      __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # set backchain to 0
+       basr    %r14,0
+1:     l       %r14,.Ldo_restart-1b(%r14)
+       basr    %r14,%r14
+
+       basr    %r14,0                          # load disabled wait PSW if
+2:     lpsw    restart_psw_crash-2b(%r14)      # do_restart returns
+       .align 4
+.Ldo_restart:
+       .long   do_restart
+.Lrestart_stack:
+       .long   restart_stack
+       .align 8
+restart_psw_crash:
+       .long   0x000a0000,0x00000000 + restart_psw_crash
+
        .section .kprobes.text, "ax"
 
 #ifdef CONFIG_CHECK_STACK
index 7a0fd426ca92a7999f5448df85e79ccacb2bde74..5f729d627cef21d96072609795481fec24cd6805 100644 (file)
@@ -865,6 +865,26 @@ restart_crash:
 restart_go:
 #endif
 
+#
+# PSW restart interrupt handler
+#
+ENTRY(psw_restart_int_handler)
+       stg     %r15,__LC_SAVE_AREA_64(%r0)     # save r15
+       larl    %r15,restart_stack              # load restart stack
+       lg      %r15,0(%r15)
+       aghi    %r15,-SP_SIZE                   # make room for pt_regs
+       stmg    %r0,%r14,SP_R0(%r15)            # store gprs %r0-%r14 to stack
+       mvc     SP_R15(8,%r15),__LC_SAVE_AREA_64(%r0)# store saved %r15 to stack
+       mvc     SP_PSW(16,%r15),__LC_RST_OLD_PSW(%r0)# store restart old psw
+       xc      __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) # set backchain to 0
+       brasl   %r14,do_restart
+
+       larl    %r14,restart_psw_crash          # load disabled wait PSW if
+       lpswe   0(%r14)                         # do_restart returns
+       .align 8
+restart_psw_crash:
+       .quad   0x0002000080000000,0x0000000000000000 + restart_psw_crash
+
        .section .kprobes.text, "ax"
 
 #ifdef CONFIG_CHECK_STACK
index a689070be287371040881afcc71b712122672a1e..04361d5a42794524419bd1898ac78ef755491449 100644 (file)
  * - halt
  * - power off
  * - reipl
+ * - restart
  */
 #define ON_PANIC_STR           "on_panic"
 #define ON_HALT_STR            "on_halt"
 #define ON_POFF_STR            "on_poff"
 #define ON_REIPL_STR           "on_reboot"
+#define ON_RESTART_STR         "on_restart"
 
 struct shutdown_action;
 struct shutdown_trigger {
@@ -1544,17 +1546,20 @@ static char vmcmd_on_reboot[128];
 static char vmcmd_on_panic[128];
 static char vmcmd_on_halt[128];
 static char vmcmd_on_poff[128];
+static char vmcmd_on_restart[128];
 
 DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n", "%s\n", vmcmd_on_reboot);
 DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n", "%s\n", vmcmd_on_panic);
 DEFINE_IPL_ATTR_STR_RW(vmcmd, on_halt, "%s\n", "%s\n", vmcmd_on_halt);
 DEFINE_IPL_ATTR_STR_RW(vmcmd, on_poff, "%s\n", "%s\n", vmcmd_on_poff);
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_restart, "%s\n", "%s\n", vmcmd_on_restart);
 
 static struct attribute *vmcmd_attrs[] = {
        &sys_vmcmd_on_reboot_attr.attr,
        &sys_vmcmd_on_panic_attr.attr,
        &sys_vmcmd_on_halt_attr.attr,
        &sys_vmcmd_on_poff_attr.attr,
+       &sys_vmcmd_on_restart_attr.attr,
        NULL,
 };
 
@@ -1576,6 +1581,8 @@ static void vmcmd_run(struct shutdown_trigger *trigger)
                cmd = vmcmd_on_halt;
        else if (strcmp(trigger->name, ON_POFF_STR) == 0)
                cmd = vmcmd_on_poff;
+       else if (strcmp(trigger->name, ON_RESTART_STR) == 0)
+               cmd = vmcmd_on_restart;
        else
                return;
 
@@ -1707,6 +1714,34 @@ static void do_panic(void)
        stop_run(&on_panic_trigger);
 }
 
+/* on restart */
+
+static struct shutdown_trigger on_restart_trigger = {ON_RESTART_STR,
+       &reipl_action};
+
+static ssize_t on_restart_show(struct kobject *kobj,
+                              struct kobj_attribute *attr, char *page)
+{
+       return sprintf(page, "%s\n", on_restart_trigger.action->name);
+}
+
+static ssize_t on_restart_store(struct kobject *kobj,
+                               struct kobj_attribute *attr,
+                               const char *buf, size_t len)
+{
+       return set_trigger(buf, &on_restart_trigger, len);
+}
+
+static struct kobj_attribute on_restart_attr =
+       __ATTR(on_restart, 0644, on_restart_show, on_restart_store);
+
+void do_restart(void)
+{
+       smp_send_stop();
+       on_restart_trigger.action->fn(&on_restart_trigger);
+       stop_run(&on_restart_trigger);
+}
+
 /* on halt */
 
 static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action};
@@ -1783,7 +1818,9 @@ static void __init shutdown_triggers_init(void)
        if (sysfs_create_file(&shutdown_actions_kset->kobj,
                              &on_poff_attr.attr))
                goto fail;
-
+       if (sysfs_create_file(&shutdown_actions_kset->kobj,
+                             &on_restart_attr.attr))
+               goto fail;
        return;
 fail:
        panic("shutdown_triggers_init failed\n");
@@ -1959,6 +1996,12 @@ static void do_reset_calls(void)
 {
        struct reset_call *reset;
 
+#ifdef CONFIG_64BIT
+       if (diag308_set_works) {
+               diag308_reset();
+               return;
+       }
+#endif
        list_for_each_entry(reset, &rcall, list)
                reset->fn();
 }
index 78eb7cfbd3d1d838ffa5a3929892aaa061f682b5..e690975403f43c9a8ac9d5dd2e8fa0ae3429722a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *    Copyright IBM Corp 2000,2009
+ *    Copyright IBM Corp 2000,2011
  *    Author(s): Holger Smolinski <Holger.Smolinski@de.ibm.com>,
  *              Denis Joseph Barrow,
  */
@@ -7,6 +7,64 @@
 #include <linux/linkage.h>
 #include <asm/asm-offsets.h>
 
+#
+# store_status
+#
+# Prerequisites to run this function:
+# - Prefix register is set to zero
+# - Original prefix register is stored in "dump_prefix_page"
+# - Lowcore protection is off
+#
+ENTRY(store_status)
+       /* Save register one and load save area base */
+       stg     %r1,__LC_SAVE_AREA_64(%r0)
+       lghi    %r1,SAVE_AREA_BASE
+       /* General purpose registers */
+       stmg    %r0,%r15,__LC_GPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       lg      %r2,__LC_SAVE_AREA_64(%r0)
+       stg     %r2,__LC_GPREGS_SAVE_AREA-SAVE_AREA_BASE+8(%r1)
+       /* Control registers */
+       stctg   %c0,%c15,__LC_CREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       /* Access registers */
+       stam    %a0,%a15,__LC_AREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       /* Floating point registers */
+       std     %f0, 0x00 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f1, 0x08 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f2, 0x10 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f3, 0x18 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f4, 0x20 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f5, 0x28 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f6, 0x30 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f7, 0x38 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f8, 0x40 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f9, 0x48 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f10,0x50 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f11,0x58 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f12,0x60 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f13,0x68 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f14,0x70 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       std     %f15,0x78 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       /* Floating point control register */
+       stfpc   __LC_FP_CREG_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       /* CPU timer */
+       stpt    __LC_CPU_TIMER_SAVE_AREA-SAVE_AREA_BASE(%r1)
+       /* Saved prefix register */
+       larl    %r2,dump_prefix_page
+       mvc     __LC_PREFIX_SAVE_AREA-SAVE_AREA_BASE(4,%r1),0(%r2)
+       /* Clock comparator - seven bytes */
+       larl    %r2,.Lclkcmp
+       stckc   0(%r2)
+       mvc     __LC_CLOCK_COMP_SAVE_AREA-SAVE_AREA_BASE + 1(7,%r1),1(%r2)
+       /* Program status word */
+       epsw    %r2,%r3
+       st      %r2,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 0(%r1)
+       st      %r3,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 4(%r1)
+       larl    %r2,store_status
+       stg     %r2,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 8(%r1)
+       br      %r14
+.align 8
+.Lclkcmp:      .quad   0x0000000000000000
+
 #
 # do_reipl_asm
 # Parameter: r2 = schid of reipl device
 ENTRY(do_reipl_asm)
                basr    %r13,0
 .Lpg0:         lpswe   .Lnewpsw-.Lpg0(%r13)
-.Lpg1:         # do store status of all registers
-
-               stg     %r1,.Lregsave-.Lpg0(%r13)
-               lghi    %r1,0x1000
-               stmg    %r0,%r15,__LC_GPREGS_SAVE_AREA-0x1000(%r1)
-               lg      %r0,.Lregsave-.Lpg0(%r13)
-               stg     %r0,__LC_GPREGS_SAVE_AREA-0x1000+8(%r1)
-               stctg   %c0,%c15,__LC_CREGS_SAVE_AREA-0x1000(%r1)
-               stam    %a0,%a15,__LC_AREGS_SAVE_AREA-0x1000(%r1)
-               lg      %r10,.Ldump_pfx-.Lpg0(%r13)
-               mvc     __LC_PREFIX_SAVE_AREA-0x1000(4,%r1),0(%r10)
-               stfpc   __LC_FP_CREG_SAVE_AREA-0x1000(%r1)
-               stckc   .Lclkcmp-.Lpg0(%r13)
-               mvc     __LC_CLOCK_COMP_SAVE_AREA-0x1000(7,%r1),.Lclkcmp-.Lpg0(%r13)
-               stpt    __LC_CPU_TIMER_SAVE_AREA-0x1000(%r1)
-               stg     %r13, __LC_PSW_SAVE_AREA-0x1000+8(%r1)
+.Lpg1:         brasl   %r14,store_status
 
                lctlg   %c6,%c6,.Lall-.Lpg0(%r13)
                lgr     %r1,%r2
@@ -67,10 +110,7 @@ ENTRY(do_reipl_asm)
                st      %r14,.Ldispsw+12-.Lpg0(%r13)
                lpswe   .Ldispsw-.Lpg0(%r13)
                .align  8
-.Lclkcmp:      .quad   0x0000000000000000
 .Lall:         .quad   0x00000000ff000000
-.Ldump_pfx:    .quad   dump_prefix_page
-.Lregsave:     .quad   0x0000000000000000
                .align  16
 /*
  * These addresses have to be 31 bit otherwise
index 0c35dee10b00e93b005114c7b0d031531b7fc253..7b371c37061de424892068ea7838a946f4f95e14 100644 (file)
@@ -346,7 +346,7 @@ setup_lowcore(void)
        lc = __alloc_bootmem_low(LC_PAGES * PAGE_SIZE, LC_PAGES * PAGE_SIZE, 0);
        lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
        lc->restart_psw.addr =
-               PSW_ADDR_AMODE | (unsigned long) restart_int_handler;
+               PSW_ADDR_AMODE | (unsigned long) psw_restart_int_handler;
        if (user_mode != HOME_SPACE_MODE)
                lc->restart_psw.mask |= PSW_ASC_HOME;
        lc->external_new_psw.mask = psw_kernel_bits;
@@ -529,6 +529,27 @@ static void __init setup_memory_end(void)
                memory_end = memory_size;
 }
 
+void *restart_stack __attribute__((__section__(".data")));
+
+/*
+ * Setup new PSW and allocate stack for PSW restart interrupt
+ */
+static void __init setup_restart_psw(void)
+{
+       psw_t psw;
+
+       restart_stack = __alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0);
+       restart_stack += ASYNC_SIZE;
+
+       /*
+        * Setup restart PSW for absolute zero lowcore. This is necesary
+        * if PSW restart is done on an offline CPU that has lowcore zero
+        */
+       psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
+       psw.addr = PSW_ADDR_AMODE | (unsigned long) psw_restart_int_handler;
+       copy_to_absolute_zero(&S390_lowcore.restart_psw, &psw, sizeof(psw));
+}
+
 static void __init
 setup_memory(void)
 {
@@ -731,6 +752,7 @@ static void __init setup_hwcaps(void)
                strcpy(elf_platform, "z10");
                break;
        case 0x2817:
+       case 0x2818:
                strcpy(elf_platform, "z196");
                break;
        }
@@ -792,6 +814,7 @@ setup_arch(char **cmdline_p)
        setup_addressing_mode();
        setup_memory();
        setup_resources();
+       setup_restart_psw();
        setup_lowcore();
 
         cpu_init();
index abbb3c3c7aaba727a2c4a1e12f6ce343aa8d4433..9a40e1cc5ec3f5e2b5ce15268b35862d22aff28e 100644 (file)
@@ -57,17 +57,15 @@ typedef struct
  */
 SYSCALL_DEFINE3(sigsuspend, int, history0, int, history1, old_sigset_t, mask)
 {
-       mask &= _BLOCKABLE;
-       spin_lock_irq(&current->sighand->siglock);
-       current->saved_sigmask = current->blocked;
-       siginitset(&current->blocked, mask);
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
+       sigset_t blocked;
 
+       current->saved_sigmask = current->blocked;
+       mask &= _BLOCKABLE;
+       siginitset(&blocked, mask);
+       set_current_blocked(&blocked);
        set_current_state(TASK_INTERRUPTIBLE);
        schedule();
-       set_thread_flag(TIF_RESTORE_SIGMASK);
-
+       set_restore_sigmask();
        return -ERESTARTNOHAND;
 }
 
@@ -172,18 +170,11 @@ SYSCALL_DEFINE0(sigreturn)
                goto badframe;
        if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE))
                goto badframe;
-
        sigdelsetmask(&set, ~_BLOCKABLE);
-       spin_lock_irq(&current->sighand->siglock);
-       current->blocked = set;
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
-
+       set_current_blocked(&set);
        if (restore_sigregs(regs, &frame->sregs))
                goto badframe;
-
        return regs->gprs[2];
-
 badframe:
        force_sig(SIGSEGV, current);
        return 0;
@@ -199,21 +190,14 @@ SYSCALL_DEFINE0(rt_sigreturn)
                goto badframe;
        if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
-
        sigdelsetmask(&set, ~_BLOCKABLE);
-       spin_lock_irq(&current->sighand->siglock);
-       current->blocked = set;
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
-
+       set_current_blocked(&set);
        if (restore_sigregs(regs, &frame->uc.uc_mcontext))
                goto badframe;
-
        if (do_sigaltstack(&frame->uc.uc_stack, NULL,
                           regs->gprs[15]) == -EFAULT)
                goto badframe;
        return regs->gprs[2];
-
 badframe:
        force_sig(SIGSEGV, current);
        return 0;
@@ -385,14 +369,11 @@ give_sigsegv:
        return -EFAULT;
 }
 
-/*
- * OK, we're invoking a handler
- */    
-
-static int
-handle_signal(unsigned long sig, struct k_sigaction *ka,
-             siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
+static int handle_signal(unsigned long sig, struct k_sigaction *ka,
+                        siginfo_t *info, sigset_t *oldset,
+                        struct pt_regs *regs)
 {
+       sigset_t blocked;
        int ret;
 
        /* Set up the stack frame */
@@ -400,17 +381,13 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
                ret = setup_rt_frame(sig, ka, info, oldset, regs);
        else
                ret = setup_frame(sig, ka, oldset, regs);
-
-       if (ret == 0) {
-               spin_lock_irq(&current->sighand->siglock);
-               sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
-               if (!(ka->sa.sa_flags & SA_NODEFER))
-                       sigaddset(&current->blocked,sig);
-               recalc_sigpending();
-               spin_unlock_irq(&current->sighand->siglock);
-       }
-
-       return ret;
+       if (ret)
+               return ret;
+       sigorsets(&blocked, &current->blocked, &ka->sa.sa_mask);
+       if (!(ka->sa.sa_flags & SA_NODEFER))
+               sigaddset(&blocked, sig);
+       set_current_blocked(&blocked);
+       return 0;
 }
 
 /*
index a6d85c0a7f209895c57015eb13964fe6673d2abc..6ab16ac64d294687b90815328f90060362abc8de 100644 (file)
@@ -452,23 +452,27 @@ out:
  */
 int __cpuinit start_secondary(void *cpuvoid)
 {
-       /* Setup the cpu */
        cpu_init();
        preempt_disable();
-       /* Enable TOD clock interrupts on the secondary cpu. */
        init_cpu_timer();
-       /* Enable cpu timer interrupts on the secondary cpu. */
        init_cpu_vtimer();
-       /* Enable pfault pseudo page faults on this cpu. */
        pfault_init();
 
-       /* call cpu notifiers */
        notify_cpu_starting(smp_processor_id());
-       /* Mark this cpu as online */
        ipi_call_lock();
        set_cpu_online(smp_processor_id(), true);
        ipi_call_unlock();
-       /* Switch on interrupts */
+       __ctl_clear_bit(0, 28); /* Disable lowcore protection */
+       S390_lowcore.restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
+       S390_lowcore.restart_psw.addr =
+               PSW_ADDR_AMODE | (unsigned long) psw_restart_int_handler;
+       __ctl_set_bit(0, 28); /* Enable lowcore protection */
+       /*
+        * Wait until the cpu which brought this one up marked it
+        * active before enabling interrupts.
+        */
+       while (!cpumask_test_cpu(smp_processor_id(), cpu_active_mask))
+               cpu_relax();
        local_irq_enable();
        /* cpu_idle will call schedule for us */
        cpu_idle();
@@ -507,7 +511,11 @@ static int __cpuinit smp_alloc_lowcore(int cpu)
        memset((char *)lowcore + 512, 0, sizeof(*lowcore) - 512);
        lowcore->async_stack = async_stack + ASYNC_SIZE;
        lowcore->panic_stack = panic_stack + PAGE_SIZE;
-
+       lowcore->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
+       lowcore->restart_psw.addr =
+               PSW_ADDR_AMODE | (unsigned long) restart_int_handler;
+       if (user_mode != HOME_SPACE_MODE)
+               lowcore->restart_psw.mask |= PSW_ASC_HOME;
 #ifndef CONFIG_64BIT
        if (MACHINE_HAS_IEEE) {
                unsigned long save_area;
index 51e5cd9b906a7887d69e2eee825ecbf38ce29d9e..5dbbaa6e594c8192302a23525bfcbfb05685c50e 100644 (file)
@@ -85,3 +85,19 @@ int memcpy_real(void *dest, void *src, size_t count)
        arch_local_irq_restore(flags);
        return rc;
 }
+
+/*
+ * Copy memory to absolute zero
+ */
+void copy_to_absolute_zero(void *dest, void *src, size_t count)
+{
+       unsigned long cr0;
+
+       BUG_ON((unsigned long) dest + count >= sizeof(struct _lowcore));
+       preempt_disable();
+       __ctl_store(cr0, 0, 0);
+       __ctl_clear_bit(0, 28); /* disable lowcore protection */
+       memcpy_real(dest + store_prefix(), src, count);
+       __ctl_load(cr0, 0, 0);
+       preempt_enable();
+}
index 2adb23938a7f5ed98e8df89d5b887c25878bf4e4..4d1f2bce87b3c5371cd49139019a36cf327b7348 100644 (file)
@@ -528,6 +528,7 @@ static inline void page_table_free_pgste(unsigned long *table)
 static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,
                                                    unsigned long vmaddr)
 {
+       return NULL;
 }
 
 static inline void page_table_free_pgste(unsigned long *table)
index 748ff1920068ae679131b8b8b9f80076c2a1f104..ff9177c8f6439eec4be3fe13a5bbea6da833b64f 100644 (file)
@@ -11,6 +11,7 @@ config SUPERH
        select HAVE_DMA_ATTRS
        select HAVE_IRQ_WORK
        select HAVE_PERF_EVENTS
+       select ARCH_HAVE_NMI_SAFE_CMPXCHG if (GUSA_RB || CPU_SH4A)
        select PERF_USE_VMALLOC
        select HAVE_KERNEL_GZIP
        select HAVE_KERNEL_BZIP2
index 84db0d6ccd0d627225c4d8fba76f6ed47edcfdd6..32114e0941ae7616aae53de88a9f79b895e973db 100644 (file)
 #include <linux/thread_info.h>
 #include <linux/irqflags.h>
 #include <linux/smp.h>
+#include <linux/cpuidle.h>
 #include <asm/pgalloc.h>
 #include <asm/system.h>
 #include <linux/atomic.h>
 #include <asm/smp.h>
 
-void (*pm_idle)(void) = NULL;
+static void (*pm_idle)(void);
 
 static int hlt_counter;
 
@@ -100,7 +101,8 @@ void cpu_idle(void)
                        local_irq_disable();
                        /* Don't trace irqs off for idle */
                        stop_critical_timings();
-                       pm_idle();
+                       if (cpuidle_idle_call())
+                               pm_idle();
                        /*
                         * Sanity check to ensure that pm_idle() returns
                         * with IRQs enabled
index 1074dddcb104595d5213849cb5656bceca80590d..42c67beadcae2367e9cb7107bb125d188b3b4443 100644 (file)
@@ -54,6 +54,7 @@ config SPARC64
        select HAVE_PERF_EVENTS
        select PERF_USE_VMALLOC
        select IRQ_PREFLOW_FASTEOI
+       select ARCH_HAVE_NMI_SAFE_CMPXCHG
 
 config ARCH_DEFCONFIG
        string
index 3c93f08ce187d7234b36f9952813f29f5a3942fa..2c2e38821f6083090e8b276689bbf70fa47a307f 100644 (file)
@@ -16,3 +16,8 @@ header-y += traps.h
 header-y += uctx.h
 header-y += utrap.h
 header-y += watchdog.h
+
+generic-y += div64.h
+generic-y += local64.h
+generic-y += irq_regs.h
+generic-y += local.h
index 325e295d60deb2b8d6efced54f06fb70c32c9279..29011cc0e4be2ef5cd7ed43db0e5c964729766dc 100644 (file)
@@ -26,61 +26,28 @@ extern void change_bit(unsigned long nr, volatile unsigned long *addr);
 #define smp_mb__before_clear_bit()     barrier()
 #define smp_mb__after_clear_bit()      barrier()
 
-#include <asm-generic/bitops/ffz.h>
-#include <asm-generic/bitops/__ffs.h>
 #include <asm-generic/bitops/fls.h>
 #include <asm-generic/bitops/__fls.h>
 #include <asm-generic/bitops/fls64.h>
 
 #ifdef __KERNEL__
 
+extern int ffs(int x);
+extern unsigned long __ffs(unsigned long);
+
+#include <asm-generic/bitops/ffz.h>
 #include <asm-generic/bitops/sched.h>
-#include <asm-generic/bitops/ffs.h>
 
 /*
  * hweightN: returns the hamming weight (i.e. the number
  * of bits set) of a N-bit word
  */
 
-#ifdef ULTRA_HAS_POPULATION_COUNT
-
-static inline unsigned int __arch_hweight64(unsigned long w)
-{
-       unsigned int res;
-
-       __asm__ ("popc %1,%0" : "=r" (res) : "r" (w));
-       return res;
-}
-
-static inline unsigned int __arch_hweight32(unsigned int w)
-{
-       unsigned int res;
-
-       __asm__ ("popc %1,%0" : "=r" (res) : "r" (w & 0xffffffff));
-       return res;
-}
+extern unsigned long __arch_hweight64(__u64 w);
+extern unsigned int __arch_hweight32(unsigned int w);
+extern unsigned int __arch_hweight16(unsigned int w);
+extern unsigned int __arch_hweight8(unsigned int w);
 
-static inline unsigned int __arch_hweight16(unsigned int w)
-{
-       unsigned int res;
-
-       __asm__ ("popc %1,%0" : "=r" (res) : "r" (w & 0xffff));
-       return res;
-}
-
-static inline unsigned int __arch_hweight8(unsigned int w)
-{
-       unsigned int res;
-
-       __asm__ ("popc %1,%0" : "=r" (res) : "r" (w & 0xff));
-       return res;
-}
-
-#else
-
-#include <asm-generic/bitops/arch_hweight.h>
-
-#endif
 #include <asm-generic/bitops/const_hweight.h>
 #include <asm-generic/bitops/lock.h>
 #endif /* __KERNEL__ */
diff --git a/arch/sparc/include/asm/div64.h b/arch/sparc/include/asm/div64.h
deleted file mode 100644 (file)
index 6cd978c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/div64.h>
index 64f7a00b37479d905dfa41c0629a8d11548c1660..7df8b7f544d47a5520065c7e452e03552b12f6b8 100644 (file)
 #define R_SPARC_6              45
 
 /* Bits present in AT_HWCAP, primarily for Sparc32.  */
-
-#define HWCAP_SPARC_FLUSH       1    /* CPU supports flush instruction. */
-#define HWCAP_SPARC_STBAR       2
-#define HWCAP_SPARC_SWAP        4
-#define HWCAP_SPARC_MULDIV      8
-#define HWCAP_SPARC_V9         16
-#define HWCAP_SPARC_ULTRA3     32
-#define HWCAP_SPARC_BLKINIT    64
-#define HWCAP_SPARC_N2         128
+#define HWCAP_SPARC_FLUSH       0x00000001
+#define HWCAP_SPARC_STBAR       0x00000002
+#define HWCAP_SPARC_SWAP        0x00000004
+#define HWCAP_SPARC_MULDIV      0x00000008
+#define HWCAP_SPARC_V9         0x00000010
+#define HWCAP_SPARC_ULTRA3     0x00000020
+#define HWCAP_SPARC_BLKINIT    0x00000040
+#define HWCAP_SPARC_N2         0x00000080
+
+/* Solaris compatible AT_HWCAP bits. */
+#define AV_SPARC_MUL32         0x00000100 /* 32x32 multiply is efficient */
+#define AV_SPARC_DIV32         0x00000200 /* 32x32 divide is efficient */
+#define AV_SPARC_FSMULD                0x00000400 /* 'fsmuld' is efficient */
+#define AV_SPARC_V8PLUS                0x00000800 /* v9 insn available to 32bit */
+#define AV_SPARC_POPC          0x00001000 /* 'popc' is efficient */
+#define AV_SPARC_VIS           0x00002000 /* VIS insns available */
+#define AV_SPARC_VIS2          0x00004000 /* VIS2 insns available */
+#define AV_SPARC_ASI_BLK_INIT  0x00008000 /* block init ASIs available */
+#define AV_SPARC_FMAF          0x00010000 /* fused multiply-add */
+#define AV_SPARC_VIS3          0x00020000 /* VIS3 insns available */
+#define AV_SPARC_HPC           0x00040000 /* HPC insns available */
+#define AV_SPARC_RANDOM                0x00080000 /* 'random' insn available */
+#define AV_SPARC_TRANS         0x00100000 /* transaction insns available */
+#define AV_SPARC_FJFMAU                0x00200000 /* unfused multiply-add */
+#define AV_SPARC_IMA           0x00400000 /* integer multiply-add */
+#define AV_SPARC_ASI_CACHE_SPARING \
+                               0x00800000 /* cache sparing ASIs available */
 
 #define CORE_DUMP_USE_REGSET
 
@@ -162,33 +180,8 @@ typedef struct {
 #define ELF_ET_DYN_BASE                0x0000010000000000UL
 #define COMPAT_ELF_ET_DYN_BASE 0x0000000070000000UL
 
-
-/* This yields a mask that user programs can use to figure out what
-   instruction set this cpu supports.  */
-
-/* On Ultra, we support all of the v8 capabilities. */
-static inline unsigned int sparc64_elf_hwcap(void)
-{
-       unsigned int cap = (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR |
-                           HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV |
-                           HWCAP_SPARC_V9);
-
-       if (tlb_type == cheetah || tlb_type == cheetah_plus)
-               cap |= HWCAP_SPARC_ULTRA3;
-       else if (tlb_type == hypervisor) {
-               if (sun4v_chip_type == SUN4V_CHIP_NIAGARA1 ||
-                   sun4v_chip_type == SUN4V_CHIP_NIAGARA2 ||
-                   sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
-                       cap |= HWCAP_SPARC_BLKINIT;
-               if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2 ||
-                   sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
-                       cap |= HWCAP_SPARC_N2;
-       }
-
-       return cap;
-}
-
-#define ELF_HWCAP      sparc64_elf_hwcap()
+extern unsigned long sparc64_elf_hwcap;
+#define ELF_HWCAP      sparc64_elf_hwcap
 
 /* This yields a string that ld.so will use to load implementation
    specific libraries for optimization.  This is more specific in
index 7a5f80df15d0e9745c1be67cda304dd6432caeb5..015a761eaa322c1a9d11d2f20281c42184daa4bc 100644 (file)
@@ -2927,6 +2927,13 @@ extern unsigned long sun4v_ncs_request(unsigned long request,
 #define HV_FAST_FIRE_GET_PERFREG       0x120
 #define HV_FAST_FIRE_SET_PERFREG       0x121
 
+#define HV_FAST_REBOOT_DATA_SET                0x172
+
+#ifndef __ASSEMBLY__
+extern unsigned long sun4v_reboot_data_set(unsigned long ra,
+                                          unsigned long len);
+#endif
+
 /* Function numbers for HV_CORE_TRAP.  */
 #define HV_CORE_SET_VER                        0x00
 #define HV_CORE_PUTCHAR                        0x01
@@ -2940,11 +2947,17 @@ extern unsigned long sun4v_ncs_request(unsigned long request,
 #define HV_GRP_CORE                    0x0001
 #define HV_GRP_INTR                    0x0002
 #define HV_GRP_SOFT_STATE              0x0003
+#define HV_GRP_TM                      0x0080
 #define HV_GRP_PCI                     0x0100
 #define HV_GRP_LDOM                    0x0101
 #define HV_GRP_SVC_CHAN                        0x0102
 #define HV_GRP_NCS                     0x0103
 #define HV_GRP_RNG                     0x0104
+#define HV_GRP_PBOOT                   0x0105
+#define HV_GRP_TPM                     0x0107
+#define HV_GRP_SDIO                    0x0108
+#define HV_GRP_SDIO_ERR                        0x0109
+#define HV_GRP_REBOOT_DATA             0x0110
 #define HV_GRP_NIAG_PERF               0x0200
 #define HV_GRP_FIRE_PERF               0x0201
 #define HV_GRP_N2_CPU                  0x0202
diff --git a/arch/sparc/include/asm/irq_regs.h b/arch/sparc/include/asm/irq_regs.h
deleted file mode 100644 (file)
index 3dd9c0b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/irq_regs.h>
diff --git a/arch/sparc/include/asm/local.h b/arch/sparc/include/asm/local.h
deleted file mode 100644 (file)
index bc80815..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _SPARC_LOCAL_H
-#define _SPARC_LOCAL_H
-
-#include <asm-generic/local.h>
-
-#endif
diff --git a/arch/sparc/include/asm/local64.h b/arch/sparc/include/asm/local64.h
deleted file mode 100644 (file)
index 36c93b5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/local64.h>
index 83c571d8c8a7ebd2fb243c9b057105dd43c827e6..1a8afd1ad04f4f64da95670e4c4bd21b01dfdb5c 100644 (file)
@@ -133,29 +133,6 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
        sub     TSB, 0x8, TSB;   \
        TSB_STORE(TSB, TAG);
 
-#define KTSB_LOAD_QUAD(TSB, REG) \
-       ldda            [TSB] ASI_NUCLEUS_QUAD_LDD, REG;
-
-#define KTSB_STORE(ADDR, VAL) \
-       stxa            VAL, [ADDR] ASI_N;
-
-#define KTSB_LOCK_TAG(TSB, REG1, REG2) \
-99:    lduwa   [TSB] ASI_N, REG1;      \
-       sethi   %hi(TSB_TAG_LOCK_HIGH), REG2;\
-       andcc   REG1, REG2, %g0;        \
-       bne,pn  %icc, 99b;              \
-        nop;                           \
-       casa    [TSB] ASI_N, REG1, REG2;\
-       cmp     REG1, REG2;             \
-       bne,pn  %icc, 99b;              \
-        nop;                           \
-
-#define KTSB_WRITE(TSB, TTE, TAG) \
-       add     TSB, 0x8, TSB;   \
-       stxa    TTE, [TSB] ASI_N;     \
-       sub     TSB, 0x8, TSB;   \
-       stxa    TAG, [TSB] ASI_N;
-
        /* Do a kernel page table walk.  Leaves physical PTE pointer in
         * REG1.  Jumps to FAIL_LABEL on early page table walk termination.
         * VADDR will not be clobbered, but REG2 will.
@@ -239,6 +216,8 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
        (KERNEL_TSB_SIZE_BYTES / 16)
 #define KERNEL_TSB4M_NENTRIES  4096
 
+#define KTSB_PHYS_SHIFT                15
+
        /* Do a kernel TSB lookup at tl>0 on VADDR+TAG, branch to OK_LABEL
         * on TSB hit.  REG1, REG2, REG3, and REG4 are used as temporaries
         * and the found TTE will be left in REG1.  REG3 and REG4 must
@@ -247,13 +226,22 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
         * VADDR and TAG will be preserved and not clobbered by this macro.
         */
 #define KERN_TSB_LOOKUP_TL1(VADDR, TAG, REG1, REG2, REG3, REG4, OK_LABEL) \
-       sethi           %hi(swapper_tsb), REG1; \
+661:   sethi           %hi(swapper_tsb), REG1;                 \
        or              REG1, %lo(swapper_tsb), REG1; \
+       .section        .swapper_tsb_phys_patch, "ax"; \
+       .word           661b; \
+       .previous; \
+661:   nop; \
+       .section        .tsb_ldquad_phys_patch, "ax"; \
+       .word           661b; \
+       sllx            REG1, KTSB_PHYS_SHIFT, REG1; \
+       sllx            REG1, KTSB_PHYS_SHIFT, REG1; \
+       .previous; \
        srlx            VADDR, PAGE_SHIFT, REG2; \
        and             REG2, (KERNEL_TSB_NENTRIES - 1), REG2; \
        sllx            REG2, 4, REG2; \
        add             REG1, REG2, REG2; \
-       KTSB_LOAD_QUAD(REG2, REG3); \
+       TSB_LOAD_QUAD(REG2, REG3); \
        cmp             REG3, TAG; \
        be,a,pt         %xcc, OK_LABEL; \
         mov            REG4, REG1;
@@ -263,12 +251,21 @@ extern struct tsb_phys_patch_entry __tsb_phys_patch, __tsb_phys_patch_end;
         * we can make use of that for the index computation.
         */
 #define KERN_TSB4M_LOOKUP_TL1(TAG, REG1, REG2, REG3, REG4, OK_LABEL) \
-       sethi           %hi(swapper_4m_tsb), REG1; \
+661:   sethi           %hi(swapper_4m_tsb), REG1;           \
        or              REG1, %lo(swapper_4m_tsb), REG1; \
+       .section        .swapper_4m_tsb_phys_patch, "ax"; \
+       .word           661b; \
+       .previous; \
+661:   nop; \
+       .section        .tsb_ldquad_phys_patch, "ax"; \
+       .word           661b; \
+       sllx            REG1, KTSB_PHYS_SHIFT, REG1; \
+       sllx            REG1, KTSB_PHYS_SHIFT, REG1; \
+       .previous; \
        and             TAG, (KERNEL_TSB4M_NENTRIES - 1), REG2; \
        sllx            REG2, 4, REG2; \
        add             REG1, REG2, REG2; \
-       KTSB_LOAD_QUAD(REG2, REG3); \
+       TSB_LOAD_QUAD(REG2, REG3); \
        cmp             REG3, TAG; \
        be,a,pt         %xcc, OK_LABEL; \
         mov            REG4, REG1;
index 17cf290dc2bcc219631323e8c2c4c1e00e4d761d..9810fd8810580d6f7ce56b3a3a1401d2d29526a2 100644 (file)
@@ -396,6 +396,7 @@ static int show_cpuinfo(struct seq_file *m, void *__unused)
                   , cpu_data(0).clock_tick
 #endif
                );
+       cpucap_info(m);
 #ifdef CONFIG_SMP
        smp_bogo(m);
 #endif
index dd1342c0a3beec2b2743a888e256bb90f677a594..490e5418740df767b693fe7a7ef023e6714f4e29 100644 (file)
 #include <linux/reboot.h>
 #include <linux/cpu.h>
 
+#include <asm/hypervisor.h>
 #include <asm/ldc.h>
 #include <asm/vio.h>
 #include <asm/mdesc.h>
 #include <asm/head.h>
 #include <asm/irq.h>
 
+#include "kernel.h"
+
 #define DRV_MODULE_NAME                "ds"
 #define PFX DRV_MODULE_NAME    ": "
 #define DRV_MODULE_VERSION     "1.0"
@@ -828,18 +831,32 @@ void ldom_set_var(const char *var, const char *value)
        }
 }
 
+static char full_boot_str[256] __attribute__((aligned(32)));
+static int reboot_data_supported;
+
 void ldom_reboot(const char *boot_command)
 {
        /* Don't bother with any of this if the boot_command
         * is empty.
         */
        if (boot_command && strlen(boot_command)) {
-               char full_boot_str[256];
+               unsigned long len;
 
                strcpy(full_boot_str, "boot ");
                strcpy(full_boot_str + strlen("boot "), boot_command);
+               len = strlen(full_boot_str);
 
-               ldom_set_var("reboot-command", full_boot_str);
+               if (reboot_data_supported) {
+                       unsigned long ra = kimage_addr_to_ra(full_boot_str);
+                       unsigned long hv_ret;
+
+                       hv_ret = sun4v_reboot_data_set(ra, len);
+                       if (hv_ret != HV_EOK)
+                               pr_err("SUN4V: Unable to set reboot data "
+                                      "hv_ret=%lu\n", hv_ret);
+               } else {
+                       ldom_set_var("reboot-command", full_boot_str);
+               }
        }
        sun4v_mach_sir();
 }
@@ -1237,6 +1254,15 @@ static struct vio_driver ds_driver = {
 
 static int __init ds_init(void)
 {
+       unsigned long hv_ret, major, minor;
+
+       hv_ret = sun4v_get_version(HV_GRP_REBOOT_DATA, &major, &minor);
+       if (hv_ret == HV_EOK) {
+               pr_info("SUN4V: Reboot data supported (maj=%lu,min=%lu).\n",
+                       major, minor);
+               reboot_data_supported = 1;
+       }
+
        kthread_run(ds_thread, NULL, "kldomd");
 
        return vio_register_driver(&ds_driver);
index d1f1361c4167d4319cac267cdb03543620bb6f7b..e27f8ea8656e3e4b9b1c799a6d170cc9739e2cb2 100644 (file)
@@ -42,6 +42,20 @@ extern void fpsave(unsigned long *fpregs, unsigned long *fsr,
 extern void fpload(unsigned long *fpregs, unsigned long *fsr);
 
 #else /* CONFIG_SPARC32 */
+struct popc_3insn_patch_entry {
+       unsigned int    addr;
+       unsigned int    insns[3];
+};
+extern struct popc_3insn_patch_entry __popc_3insn_patch,
+       __popc_3insn_patch_end;
+
+struct popc_6insn_patch_entry {
+       unsigned int    addr;
+       unsigned int    insns[6];
+};
+extern struct popc_6insn_patch_entry __popc_6insn_patch,
+       __popc_6insn_patch_end;
+
 extern void __init per_cpu_patch(void);
 extern void __init sun4v_patch(void);
 extern void __init boot_cpu_id_too_large(int cpu);
index c752603a7c0dc4a4ca5b560dcbd0189b3eb4b329..0eac1b2fc53d42ab2d0cf4c9d5e54e4a46e348f8 100644 (file)
@@ -559,7 +559,7 @@ niagara2_patch:
         nop
        call    niagara_patch_bzero
         nop
-       call    niagara2_patch_pageops
+       call    niagara_patch_pageops
         nop
 
        ba,a,pt %xcc, 80f
index d306e648c33cb15ee2cdab960a906bdccf32df61..c2d055d8ba9e273ed8e996c7133b553e75ffcbc7 100644 (file)
@@ -28,11 +28,17 @@ static struct api_info api_table[] = {
        { .group = HV_GRP_CORE,         .flags = FLAG_PRE_API   },
        { .group = HV_GRP_INTR,                                 },
        { .group = HV_GRP_SOFT_STATE,                           },
+       { .group = HV_GRP_TM,                                   },
        { .group = HV_GRP_PCI,          .flags = FLAG_PRE_API   },
        { .group = HV_GRP_LDOM,                                 },
        { .group = HV_GRP_SVC_CHAN,     .flags = FLAG_PRE_API   },
        { .group = HV_GRP_NCS,          .flags = FLAG_PRE_API   },
        { .group = HV_GRP_RNG,                                  },
+       { .group = HV_GRP_PBOOT,                                },
+       { .group = HV_GRP_TPM,                                  },
+       { .group = HV_GRP_SDIO,                                 },
+       { .group = HV_GRP_SDIO_ERR,                             },
+       { .group = HV_GRP_REBOOT_DATA,                          },
        { .group = HV_GRP_NIAG_PERF,    .flags = FLAG_PRE_API   },
        { .group = HV_GRP_FIRE_PERF,                            },
        { .group = HV_GRP_N2_CPU,                               },
index 8a5f35ffb15ef622ddc9a0e8ecdc5c2a26142c01..58d60de4d65b496d1287de3468d58e2cb08e6075 100644 (file)
@@ -798,3 +798,10 @@ ENTRY(sun4v_niagara2_setperf)
        retl
         nop
 ENDPROC(sun4v_niagara2_setperf)
+
+ENTRY(sun4v_reboot_data_set)
+       mov     HV_FAST_REBOOT_DATA_SET, %o5
+       ta      HV_FAST_TRAP
+       retl
+        nop
+ENDPROC(sun4v_reboot_data_set)
index 6f6544cfa0ef589cf40da84b22bb867148af1e3a..fd6c36b1df743140c2aaa5d4139feec5075427c7 100644 (file)
@@ -4,12 +4,27 @@
 #include <linux/interrupt.h>
 
 #include <asm/traps.h>
+#include <asm/head.h>
+#include <asm/io.h>
 
 /* cpu.c */
 extern const char *sparc_pmu_type;
 extern unsigned int fsr_storage;
 extern int ncpus_probed;
 
+#ifdef CONFIG_SPARC64
+/* setup_64.c */
+struct seq_file;
+extern void cpucap_info(struct seq_file *);
+
+static inline unsigned long kimage_addr_to_ra(const char *p)
+{
+       unsigned long val = (unsigned long) p;
+
+       return kern_base + (val - KERNBASE);
+}
+#endif
+
 #ifdef CONFIG_SPARC32
 /* cpu.c */
 extern void cpu_probe(void);
index 1d361477d7d639ebe871a8b36243cd42d689b54d..79f310364849af8f191e18414d4d9147a9ec8a1e 100644 (file)
@@ -47,16 +47,16 @@ kvmap_itlb_tsb_miss:
 kvmap_itlb_vmalloc_addr:
        KERN_PGTABLE_WALK(%g4, %g5, %g2, kvmap_itlb_longpath)
 
-       KTSB_LOCK_TAG(%g1, %g2, %g7)
+       TSB_LOCK_TAG(%g1, %g2, %g7)
 
        /* Load and check PTE.  */
        ldxa            [%g5] ASI_PHYS_USE_EC, %g5
        mov             1, %g7
        sllx            %g7, TSB_TAG_INVALID_BIT, %g7
        brgez,a,pn      %g5, kvmap_itlb_longpath
-        KTSB_STORE(%g1, %g7)
+        TSB_STORE(%g1, %g7)
 
-       KTSB_WRITE(%g1, %g5, %g6)
+       TSB_WRITE(%g1, %g5, %g6)
 
        /* fallthrough to TLB load */
 
@@ -102,9 +102,9 @@ kvmap_itlb_longpath:
 kvmap_itlb_obp:
        OBP_TRANS_LOOKUP(%g4, %g5, %g2, %g3, kvmap_itlb_longpath)
 
-       KTSB_LOCK_TAG(%g1, %g2, %g7)
+       TSB_LOCK_TAG(%g1, %g2, %g7)
 
-       KTSB_WRITE(%g1, %g5, %g6)
+       TSB_WRITE(%g1, %g5, %g6)
 
        ba,pt           %xcc, kvmap_itlb_load
         nop
@@ -112,17 +112,17 @@ kvmap_itlb_obp:
 kvmap_dtlb_obp:
        OBP_TRANS_LOOKUP(%g4, %g5, %g2, %g3, kvmap_dtlb_longpath)
 
-       KTSB_LOCK_TAG(%g1, %g2, %g7)
+       TSB_LOCK_TAG(%g1, %g2, %g7)
 
-       KTSB_WRITE(%g1, %g5, %g6)
+       TSB_WRITE(%g1, %g5, %g6)
 
        ba,pt           %xcc, kvmap_dtlb_load
         nop
 
        .align          32
 kvmap_dtlb_tsb4m_load:
-       KTSB_LOCK_TAG(%g1, %g2, %g7)
-       KTSB_WRITE(%g1, %g5, %g6)
+       TSB_LOCK_TAG(%g1, %g2, %g7)
+       TSB_WRITE(%g1, %g5, %g6)
        ba,pt           %xcc, kvmap_dtlb_load
         nop
 
@@ -222,16 +222,16 @@ kvmap_linear_patch:
 kvmap_dtlb_vmalloc_addr:
        KERN_PGTABLE_WALK(%g4, %g5, %g2, kvmap_dtlb_longpath)
 
-       KTSB_LOCK_TAG(%g1, %g2, %g7)
+       TSB_LOCK_TAG(%g1, %g2, %g7)
 
        /* Load and check PTE.  */
        ldxa            [%g5] ASI_PHYS_USE_EC, %g5
        mov             1, %g7
        sllx            %g7, TSB_TAG_INVALID_BIT, %g7
        brgez,a,pn      %g5, kvmap_dtlb_longpath
-        KTSB_STORE(%g1, %g7)
+        TSB_STORE(%g1, %g7)
 
-       KTSB_WRITE(%g1, %g5, %g6)
+       TSB_WRITE(%g1, %g5, %g6)
 
        /* fallthrough to TLB load */
 
index 42f28c7420e1d99a1ac5abc3d44bc45a3e5fea54..acaebb63c4fd4b7a7f42e0fc6d40befe799168c3 100644 (file)
@@ -508,6 +508,8 @@ const char *mdesc_node_name(struct mdesc_handle *hp, u64 node)
 }
 EXPORT_SYMBOL(mdesc_node_name);
 
+static u64 max_cpus = 64;
+
 static void __init report_platform_properties(void)
 {
        struct mdesc_handle *hp = mdesc_grab();
@@ -543,8 +545,10 @@ static void __init report_platform_properties(void)
        if (v)
                printk("PLATFORM: watchdog-max-timeout [%llu ms]\n", *v);
        v = mdesc_get_property(hp, pn, "max-cpus", NULL);
-       if (v)
-               printk("PLATFORM: max-cpus [%llu]\n", *v);
+       if (v) {
+               max_cpus = *v;
+               printk("PLATFORM: max-cpus [%llu]\n", max_cpus);
+       }
 
 #ifdef CONFIG_SMP
        {
@@ -715,7 +719,7 @@ static void __cpuinit set_proc_ids(struct mdesc_handle *hp)
 }
 
 static void __cpuinit get_one_mondo_bits(const u64 *p, unsigned int *mask,
-                                        unsigned char def)
+                                        unsigned long def, unsigned long max)
 {
        u64 val;
 
@@ -726,6 +730,9 @@ static void __cpuinit get_one_mondo_bits(const u64 *p, unsigned int *mask,
        if (!val || val >= 64)
                goto use_default;
 
+       if (val > max)
+               val = max;
+
        *mask = ((1U << val) * 64U) - 1U;
        return;
 
@@ -736,19 +743,28 @@ use_default:
 static void __cpuinit get_mondo_data(struct mdesc_handle *hp, u64 mp,
                                     struct trap_per_cpu *tb)
 {
+       static int printed;
        const u64 *val;
 
        val = mdesc_get_property(hp, mp, "q-cpu-mondo-#bits", NULL);
-       get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7);
+       get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7, ilog2(max_cpus * 2));
 
        val = mdesc_get_property(hp, mp, "q-dev-mondo-#bits", NULL);
-       get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7);
+       get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7, 8);
 
        val = mdesc_get_property(hp, mp, "q-resumable-#bits", NULL);
-       get_one_mondo_bits(val, &tb->resum_qmask, 6);
+       get_one_mondo_bits(val, &tb->resum_qmask, 6, 7);
 
        val = mdesc_get_property(hp, mp, "q-nonresumable-#bits", NULL);
-       get_one_mondo_bits(val, &tb->nonresum_qmask, 2);
+       get_one_mondo_bits(val, &tb->nonresum_qmask, 2, 2);
+       if (!printed++) {
+               pr_info("SUN4V: Mondo queue sizes "
+                       "[cpu(%u) dev(%u) r(%u) nr(%u)]\n",
+                       tb->cpu_mondo_qmask + 1,
+                       tb->dev_mondo_qmask + 1,
+                       tb->resum_qmask + 1,
+                       tb->nonresum_qmask + 1);
+       }
 }
 
 static void * __cpuinit mdesc_iterate_over_cpus(void *(*func)(struct mdesc_handle *, u64, int, void *), void *arg, cpumask_t *mask)
index c4dd0999da86b633568f278cada4420593b55940..3e9daea1653d38afcfee2f04d70d79af732c6c2d 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/interrupt.h>
 #include <linux/cpu.h>
 #include <linux/initrd.h>
+#include <linux/module.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -46,6 +47,8 @@
 #include <asm/mmu.h>
 #include <asm/ns87303.h>
 #include <asm/btext.h>
+#include <asm/elf.h>
+#include <asm/mdesc.h>
 
 #ifdef CONFIG_IP_PNP
 #include <net/ipconfig.h>
@@ -269,6 +272,40 @@ void __init sun4v_patch(void)
        sun4v_hvapi_init();
 }
 
+static void __init popc_patch(void)
+{
+       struct popc_3insn_patch_entry *p3;
+       struct popc_6insn_patch_entry *p6;
+
+       p3 = &__popc_3insn_patch;
+       while (p3 < &__popc_3insn_patch_end) {
+               unsigned long i, addr = p3->addr;
+
+               for (i = 0; i < 3; i++) {
+                       *(unsigned int *) (addr +  (i * 4)) = p3->insns[i];
+                       wmb();
+                       __asm__ __volatile__("flush     %0"
+                                            : : "r" (addr +  (i * 4)));
+               }
+
+               p3++;
+       }
+
+       p6 = &__popc_6insn_patch;
+       while (p6 < &__popc_6insn_patch_end) {
+               unsigned long i, addr = p6->addr;
+
+               for (i = 0; i < 6; i++) {
+                       *(unsigned int *) (addr +  (i * 4)) = p6->insns[i];
+                       wmb();
+                       __asm__ __volatile__("flush     %0"
+                                            : : "r" (addr +  (i * 4)));
+               }
+
+               p6++;
+       }
+}
+
 #ifdef CONFIG_SMP
 void __init boot_cpu_id_too_large(int cpu)
 {
@@ -278,6 +315,154 @@ void __init boot_cpu_id_too_large(int cpu)
 }
 #endif
 
+/* On Ultra, we support all of the v8 capabilities. */
+unsigned long sparc64_elf_hwcap = (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR |
+                                  HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV |
+                                  HWCAP_SPARC_V9);
+EXPORT_SYMBOL(sparc64_elf_hwcap);
+
+static const char *hwcaps[] = {
+       "flush", "stbar", "swap", "muldiv", "v9",
+       "ultra3", "blkinit", "n2",
+
+       /* These strings are as they appear in the machine description
+        * 'hwcap-list' property for cpu nodes.
+        */
+       "mul32", "div32", "fsmuld", "v8plus", "popc", "vis", "vis2",
+       "ASIBlkInit", "fmaf", "vis3", "hpc", "random", "trans", "fjfmau",
+       "ima", "cspare",
+};
+
+void cpucap_info(struct seq_file *m)
+{
+       unsigned long caps = sparc64_elf_hwcap;
+       int i, printed = 0;
+
+       seq_puts(m, "cpucaps\t\t: ");
+       for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
+               unsigned long bit = 1UL << i;
+               if (caps & bit) {
+                       seq_printf(m, "%s%s",
+                                  printed ? "," : "", hwcaps[i]);
+                       printed++;
+               }
+       }
+       seq_putc(m, '\n');
+}
+
+static void __init report_hwcaps(unsigned long caps)
+{
+       int i, printed = 0;
+
+       printk(KERN_INFO "CPU CAPS: [");
+       for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
+               unsigned long bit = 1UL << i;
+               if (caps & bit) {
+                       printk(KERN_CONT "%s%s",
+                              printed ? "," : "", hwcaps[i]);
+                       if (++printed == 8) {
+                               printk(KERN_CONT "]\n");
+                               printk(KERN_INFO "CPU CAPS: [");
+                               printed = 0;
+                       }
+               }
+       }
+       printk(KERN_CONT "]\n");
+}
+
+static unsigned long __init mdesc_cpu_hwcap_list(void)
+{
+       struct mdesc_handle *hp;
+       unsigned long caps = 0;
+       const char *prop;
+       int len;
+       u64 pn;
+
+       hp = mdesc_grab();
+       if (!hp)
+               return 0;
+
+       pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "cpu");
+       if (pn == MDESC_NODE_NULL)
+               goto out;
+
+       prop = mdesc_get_property(hp, pn, "hwcap-list", &len);
+       if (!prop)
+               goto out;
+
+       while (len) {
+               int i, plen;
+
+               for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
+                       unsigned long bit = 1UL << i;
+
+                       if (!strcmp(prop, hwcaps[i])) {
+                               caps |= bit;
+                               break;
+                       }
+               }
+
+               plen = strlen(prop) + 1;
+               prop += plen;
+               len -= plen;
+       }
+
+out:
+       mdesc_release(hp);
+       return caps;
+}
+
+/* This yields a mask that user programs can use to figure out what
+ * instruction set this cpu supports.
+ */
+static void __init init_sparc64_elf_hwcap(void)
+{
+       unsigned long cap = sparc64_elf_hwcap;
+       unsigned long mdesc_caps;
+
+       if (tlb_type == cheetah || tlb_type == cheetah_plus)
+               cap |= HWCAP_SPARC_ULTRA3;
+       else if (tlb_type == hypervisor) {
+               if (sun4v_chip_type == SUN4V_CHIP_NIAGARA1 ||
+                   sun4v_chip_type == SUN4V_CHIP_NIAGARA2 ||
+                   sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
+                       cap |= HWCAP_SPARC_BLKINIT;
+               if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2 ||
+                   sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
+                       cap |= HWCAP_SPARC_N2;
+       }
+
+       cap |= (AV_SPARC_MUL32 | AV_SPARC_DIV32 | AV_SPARC_V8PLUS);
+
+       mdesc_caps = mdesc_cpu_hwcap_list();
+       if (!mdesc_caps) {
+               if (tlb_type == spitfire)
+                       cap |= AV_SPARC_VIS;
+               if (tlb_type == cheetah || tlb_type == cheetah_plus)
+                       cap |= AV_SPARC_VIS | AV_SPARC_VIS2;
+               if (tlb_type == cheetah_plus)
+                       cap |= AV_SPARC_POPC;
+               if (tlb_type == hypervisor) {
+                       if (sun4v_chip_type == SUN4V_CHIP_NIAGARA1)
+                               cap |= AV_SPARC_ASI_BLK_INIT;
+                       if (sun4v_chip_type == SUN4V_CHIP_NIAGARA2 ||
+                           sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
+                               cap |= (AV_SPARC_VIS | AV_SPARC_VIS2 |
+                                       AV_SPARC_ASI_BLK_INIT |
+                                       AV_SPARC_POPC);
+                       if (sun4v_chip_type == SUN4V_CHIP_NIAGARA3)
+                               cap |= (AV_SPARC_VIS3 | AV_SPARC_HPC |
+                                       AV_SPARC_FMAF);
+               }
+       }
+       sparc64_elf_hwcap = cap | mdesc_caps;
+
+       report_hwcaps(sparc64_elf_hwcap);
+
+       if (sparc64_elf_hwcap & AV_SPARC_POPC)
+               popc_patch();
+}
+
 void __init setup_arch(char **cmdline_p)
 {
        /* Initialize PROM console and command line. */
@@ -337,6 +522,7 @@ void __init setup_arch(char **cmdline_p)
        init_cur_cpu_trap(current_thread_info());
 
        paging_init();
+       init_sparc64_elf_hwcap();
 }
 
 extern int stop_a_enabled;
index 372ad59c4cba3ee65ed8f81d9d1a7ca1e41956c2..83b47ab02d9611f0789c5b6eb8c13f39c8ce078e 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/init.h>
+#include <linux/bitops.h>
 
 #include <asm/system.h>
 #include <asm/cpudata.h>
@@ -38,5 +39,15 @@ EXPORT_SYMBOL(sun4v_niagara_setperf);
 EXPORT_SYMBOL(sun4v_niagara2_getperf);
 EXPORT_SYMBOL(sun4v_niagara2_setperf);
 
+/* from hweight.S */
+EXPORT_SYMBOL(__arch_hweight8);
+EXPORT_SYMBOL(__arch_hweight16);
+EXPORT_SYMBOL(__arch_hweight32);
+EXPORT_SYMBOL(__arch_hweight64);
+
+/* from ffs_ffz.S */
+EXPORT_SYMBOL(ffs);
+EXPORT_SYMBOL(__ffs);
+
 /* Exporting a symbol from /init/main.c */
 EXPORT_SYMBOL(saved_command_line);
index 8cdbe5946b43eb6afde994aea6e36e47c58f51b6..c59af546f522999342361a5babcb64a1dfab3ccd 100644 (file)
 #include <asm/head.h>
 #include <asm/io.h>
 
-static int hv_supports_soft_state;
-
-static unsigned long kimage_addr_to_ra(const char *p)
-{
-       unsigned long val = (unsigned long) p;
+#include "kernel.h"
 
-       return kern_base + (val - KERNBASE);
-}
+static int hv_supports_soft_state;
 
 static void do_set_sstate(unsigned long state, const char *msg)
 {
index 35cff1673aa4ec320cd25fc86b660eb88cc28cb8..76e4ac1a13e1b9978be67102ed45ef5f0f19e274 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/bitops.h>
 #include <linux/perf_event.h>
 #include <linux/ratelimit.h>
+#include <linux/bitops.h>
 #include <asm/fpumacro.h>
 
 enum direction {
@@ -373,16 +374,11 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn)
        }
 }
 
-static char popc_helper[] = {
-0, 1, 1, 2, 1, 2, 2, 3,
-1, 2, 2, 3, 2, 3, 3, 4, 
-};
-
 int handle_popc(u32 insn, struct pt_regs *regs)
 {
-       u64 value;
-       int ret, i, rd = ((insn >> 25) & 0x1f);
        int from_kernel = (regs->tstate & TSTATE_PRIV) != 0;
+       int ret, rd = ((insn >> 25) & 0x1f);
+       u64 value;
                                
        perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
        if (insn & 0x2000) {
@@ -392,10 +388,7 @@ int handle_popc(u32 insn, struct pt_regs *regs)
                maybe_flush_windows(0, insn & 0x1f, rd, from_kernel);
                value = fetch_reg(insn & 0x1f, regs);
        }
-       for (ret = 0, i = 0; i < 16; i++) {
-               ret += popc_helper[value & 0xf];
-               value >>= 4;
-       }
+       ret = hweight64(value);
        if (rd < 16) {
                if (rd)
                        regs->u_regs[rd] = ret;
index c0220759003e13d302a52ebd1ec5e75dd641c531..0e1605697b4905b030923be4d18ef2efa47fe423 100644 (file)
@@ -107,7 +107,26 @@ SECTIONS
                *(.sun4v_2insn_patch)
                __sun4v_2insn_patch_end = .;
        }
-
+       .swapper_tsb_phys_patch : {
+               __swapper_tsb_phys_patch = .;
+               *(.swapper_tsb_phys_patch)
+               __swapper_tsb_phys_patch_end = .;
+       }
+       .swapper_4m_tsb_phys_patch : {
+               __swapper_4m_tsb_phys_patch = .;
+               *(.swapper_4m_tsb_phys_patch)
+               __swapper_4m_tsb_phys_patch_end = .;
+       }
+       .popc_3insn_patch : {
+               __popc_3insn_patch = .;
+               *(.popc_3insn_patch)
+               __popc_3insn_patch_end = .;
+       }
+       .popc_6insn_patch : {
+               __popc_6insn_patch = .;
+               *(.popc_6insn_patch)
+               __popc_6insn_patch_end = .;
+       }
        PERCPU_SECTION(SMP_CACHE_BYTES)
 
        . = ALIGN(PAGE_SIZE);
index 7f01b8fce8bcff65e77709806157a28b854ae51b..a3fc4375a150e9cd637202eefc5d80598db17dce 100644 (file)
@@ -31,13 +31,13 @@ lib-$(CONFIG_SPARC64) += NGmemcpy.o NGcopy_from_user.o NGcopy_to_user.o
 lib-$(CONFIG_SPARC64) += NGpatch.o NGpage.o NGbzero.o
 
 lib-$(CONFIG_SPARC64) += NG2memcpy.o NG2copy_from_user.o NG2copy_to_user.o
-lib-$(CONFIG_SPARC64) +=  NG2patch.o NG2page.o
+lib-$(CONFIG_SPARC64) +=  NG2patch.o
 
 lib-$(CONFIG_SPARC64) += GENmemcpy.o GENcopy_from_user.o GENcopy_to_user.o
 lib-$(CONFIG_SPARC64) += GENpatch.o GENpage.o GENbzero.o
 
 lib-$(CONFIG_SPARC64) += copy_in_user.o user_fixup.o memmove.o
-lib-$(CONFIG_SPARC64) += mcount.o ipcsum.o xor.o
+lib-$(CONFIG_SPARC64) += mcount.o ipcsum.o xor.o hweight.o ffs.o
 
 obj-y                 += iomap.o
 obj-$(CONFIG_SPARC32) += atomic32.o
diff --git a/arch/sparc/lib/NG2page.S b/arch/sparc/lib/NG2page.S
deleted file mode 100644 (file)
index 73b6b7c..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/* NG2page.S: Niagara-2 optimized clear and copy page.
- *
- * Copyright (C) 2007 (davem@davemloft.net)
- */
-
-#include <asm/asi.h>
-#include <asm/page.h>
-#include <asm/visasm.h>
-
-       .text
-       .align  32
-
-       /* This is heavily simplified from the sun4u variants
-        * because Niagara-2 does not have any D-cache aliasing issues.
-        */
-NG2copy_user_page:     /* %o0=dest, %o1=src, %o2=vaddr */
-       prefetch        [%o1 + 0x00], #one_read
-       prefetch        [%o1 + 0x40], #one_read
-       VISEntryHalf
-       set             PAGE_SIZE, %g7
-       sub             %o0, %o1, %g3
-1:     stxa            %g0, [%o1 + %g3] ASI_BLK_INIT_QUAD_LDD_P
-       subcc           %g7, 64, %g7
-       ldda            [%o1] ASI_BLK_P, %f0
-       stda            %f0, [%o1 + %g3] ASI_BLK_P
-       add             %o1, 64, %o1
-       bne,pt          %xcc, 1b
-        prefetch       [%o1 + 0x40], #one_read
-       membar          #Sync
-       VISExitHalf
-       retl
-        nop
-
-#define BRANCH_ALWAYS  0x10680000
-#define NOP            0x01000000
-#define NG_DO_PATCH(OLD, NEW)  \
-       sethi   %hi(NEW), %g1; \
-       or      %g1, %lo(NEW), %g1; \
-       sethi   %hi(OLD), %g2; \
-       or      %g2, %lo(OLD), %g2; \
-       sub     %g1, %g2, %g1; \
-       sethi   %hi(BRANCH_ALWAYS), %g3; \
-       sll     %g1, 11, %g1; \
-       srl     %g1, 11 + 2, %g1; \
-       or      %g3, %lo(BRANCH_ALWAYS), %g3; \
-       or      %g3, %g1, %g3; \
-       stw     %g3, [%g2]; \
-       sethi   %hi(NOP), %g3; \
-       or      %g3, %lo(NOP), %g3; \
-       stw     %g3, [%g2 + 0x4]; \
-       flush   %g2;
-
-       .globl  niagara2_patch_pageops
-       .type   niagara2_patch_pageops,#function
-niagara2_patch_pageops:
-       NG_DO_PATCH(copy_user_page, NG2copy_user_page)
-       NG_DO_PATCH(_clear_page, NGclear_page)
-       NG_DO_PATCH(clear_user_page, NGclear_user_page)
-       retl
-        nop
-       .size   niagara2_patch_pageops,.-niagara2_patch_pageops
index 428920de05baeb4f87b80a93d3a09480ef19ffec..b9e790b9c6b8c41356883593f3e70e81ea2733c7 100644 (file)
         */
 
 NGcopy_user_page:      /* %o0=dest, %o1=src, %o2=vaddr */
-       prefetch        [%o1 + 0x00], #one_read
-       mov             8, %g1
-       mov             16, %g2
-       mov             24, %g3
+       save            %sp, -192, %sp
+       rd              %asi, %g3
+       wr              %g0, ASI_BLK_INIT_QUAD_LDD_P, %asi
        set             PAGE_SIZE, %g7
+       prefetch        [%i1 + 0x00], #one_read
+       prefetch        [%i1 + 0x40], #one_read
 
-1:     ldda            [%o1 + %g0] ASI_BLK_INIT_QUAD_LDD_P, %o2
-       ldda            [%o1 + %g2] ASI_BLK_INIT_QUAD_LDD_P, %o4
-       prefetch        [%o1 + 0x40], #one_read
-       add             %o1, 32, %o1
-       stxa            %o2, [%o0 + %g0] ASI_BLK_INIT_QUAD_LDD_P
-       stxa            %o3, [%o0 + %g1] ASI_BLK_INIT_QUAD_LDD_P
-       ldda            [%o1 + %g0] ASI_BLK_INIT_QUAD_LDD_P, %o2
-       stxa            %o4, [%o0 + %g2] ASI_BLK_INIT_QUAD_LDD_P
-       stxa            %o5, [%o0 + %g3] ASI_BLK_INIT_QUAD_LDD_P
-       ldda            [%o1 + %g2] ASI_BLK_INIT_QUAD_LDD_P, %o4
-       add             %o1, 32, %o1
-       add             %o0, 32, %o0
-       stxa            %o2, [%o0 + %g0] ASI_BLK_INIT_QUAD_LDD_P
-       stxa            %o3, [%o0 + %g1] ASI_BLK_INIT_QUAD_LDD_P
-       stxa            %o4, [%o0 + %g2] ASI_BLK_INIT_QUAD_LDD_P
-       stxa            %o5, [%o0 + %g3] ASI_BLK_INIT_QUAD_LDD_P
-       subcc           %g7, 64, %g7
+1:     prefetch        [%i1 + 0x80], #one_read
+       prefetch        [%i1 + 0xc0], #one_read
+       ldda            [%i1 + 0x00] %asi, %o2
+       ldda            [%i1 + 0x10] %asi, %o4
+       ldda            [%i1 + 0x20] %asi, %l2
+       ldda            [%i1 + 0x30] %asi, %l4
+       stxa            %o2, [%i0 + 0x00] %asi
+       stxa            %o3, [%i0 + 0x08] %asi
+       stxa            %o4, [%i0 + 0x10] %asi
+       stxa            %o5, [%i0 + 0x18] %asi
+       stxa            %l2, [%i0 + 0x20] %asi
+       stxa            %l3, [%i0 + 0x28] %asi
+       stxa            %l4, [%i0 + 0x30] %asi
+       stxa            %l5, [%i0 + 0x38] %asi
+       ldda            [%i1 + 0x40] %asi, %o2
+       ldda            [%i1 + 0x50] %asi, %o4
+       ldda            [%i1 + 0x60] %asi, %l2
+       ldda            [%i1 + 0x70] %asi, %l4
+       stxa            %o2, [%i0 + 0x40] %asi
+       stxa            %o3, [%i0 + 0x48] %asi
+       stxa            %o4, [%i0 + 0x50] %asi
+       stxa            %o5, [%i0 + 0x58] %asi
+       stxa            %l2, [%i0 + 0x60] %asi
+       stxa            %l3, [%i0 + 0x68] %asi
+       stxa            %l4, [%i0 + 0x70] %asi
+       stxa            %l5, [%i0 + 0x78] %asi
+       add             %i1, 128, %i1
+       subcc           %g7, 128, %g7
        bne,pt          %xcc, 1b
-        add            %o0, 32, %o0
+        add            %i0, 128, %i0
+       wr              %g3, 0x0, %asi
        membar          #Sync
-       retl
-        nop
+       ret
+        restore
 
-       .globl          NGclear_page, NGclear_user_page
+       .align          32
 NGclear_page:          /* %o0=dest */
 NGclear_user_page:     /* %o0=dest, %o1=vaddr */
-       mov             8, %g1
-       mov             16, %g2
-       mov             24, %g3
+       rd              %asi, %g3
+       wr              %g0, ASI_BLK_INIT_QUAD_LDD_P, %asi
        set             PAGE_SIZE, %g7
 
-1:     stxa            %g0, [%o0 + %g0] ASI_BLK_INIT_QUAD_LDD_P
-       stxa            %g0, [%o0 + %g1] ASI_BLK_INIT_QUAD_LDD_P
-       stxa            %g0, [%o0 + %g2] ASI_BLK_INIT_QUAD_LDD_P
-       stxa            %g0, [%o0 + %g3] ASI_BLK_INIT_QUAD_LDD_P
-       add             %o0, 32, %o0
-       stxa            %g0, [%o0 + %g0] ASI_BLK_INIT_QUAD_LDD_P
-       stxa            %g0, [%o0 + %g1] ASI_BLK_INIT_QUAD_LDD_P
-       stxa            %g0, [%o0 + %g2] ASI_BLK_INIT_QUAD_LDD_P
-       stxa            %g0, [%o0 + %g3] ASI_BLK_INIT_QUAD_LDD_P
-       subcc           %g7, 64, %g7
+1:     stxa            %g0, [%o0 + 0x00] %asi
+       stxa            %g0, [%o0 + 0x08] %asi
+       stxa            %g0, [%o0 + 0x10] %asi
+       stxa            %g0, [%o0 + 0x18] %asi
+       stxa            %g0, [%o0 + 0x20] %asi
+       stxa            %g0, [%o0 + 0x28] %asi
+       stxa            %g0, [%o0 + 0x30] %asi
+       stxa            %g0, [%o0 + 0x38] %asi
+       stxa            %g0, [%o0 + 0x40] %asi
+       stxa            %g0, [%o0 + 0x48] %asi
+       stxa            %g0, [%o0 + 0x50] %asi
+       stxa            %g0, [%o0 + 0x58] %asi
+       stxa            %g0, [%o0 + 0x60] %asi
+       stxa            %g0, [%o0 + 0x68] %asi
+       stxa            %g0, [%o0 + 0x70] %asi
+       stxa            %g0, [%o0 + 0x78] %asi
+       stxa            %g0, [%o0 + 0x80] %asi
+       stxa            %g0, [%o0 + 0x88] %asi
+       stxa            %g0, [%o0 + 0x90] %asi
+       stxa            %g0, [%o0 + 0x98] %asi
+       stxa            %g0, [%o0 + 0xa0] %asi
+       stxa            %g0, [%o0 + 0xa8] %asi
+       stxa            %g0, [%o0 + 0xb0] %asi
+       stxa            %g0, [%o0 + 0xb8] %asi
+       stxa            %g0, [%o0 + 0xc0] %asi
+       stxa            %g0, [%o0 + 0xc8] %asi
+       stxa            %g0, [%o0 + 0xd0] %asi
+       stxa            %g0, [%o0 + 0xd8] %asi
+       stxa            %g0, [%o0 + 0xe0] %asi
+       stxa            %g0, [%o0 + 0xe8] %asi
+       stxa            %g0, [%o0 + 0xf0] %asi
+       stxa            %g0, [%o0 + 0xf8] %asi
+       subcc           %g7, 256, %g7
        bne,pt          %xcc, 1b
-        add            %o0, 32, %o0
+        add            %o0, 256, %o0
+       wr              %g3, 0x0, %asi
        membar          #Sync
        retl
         nop
index 8600eb2461b5628f6569d731c5713944c8a1c242..1d32b54089aad3e3d6094f9d3d013b4f3664602b 100644 (file)
@@ -65,7 +65,7 @@ int __atomic_add_unless(atomic_t *v, int a, int u)
        if (ret != u)
                v->counter += a;
        spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
-       return ret != u;
+       return ret;
 }
 EXPORT_SYMBOL(__atomic_add_unless);
 
diff --git a/arch/sparc/lib/ffs.S b/arch/sparc/lib/ffs.S
new file mode 100644 (file)
index 0000000..b39389f
--- /dev/null
@@ -0,0 +1,84 @@
+#include <linux/linkage.h>
+
+       .register       %g2,#scratch
+
+       .text
+       .align  32
+
+ENTRY(ffs)
+       brnz,pt %o0, 1f
+        mov    1, %o1
+       retl
+        clr    %o0
+       nop
+       nop
+ENTRY(__ffs)
+       sllx    %o0, 32, %g1            /* 1  */
+       srlx    %o0, 32, %g2
+
+       clr     %o1                     /* 2  */
+       movrz   %g1, %g2, %o0
+
+       movrz   %g1, 32, %o1            /* 3  */
+1:     clr     %o2
+
+       sllx    %o0, (64 - 16), %g1     /* 4  */
+       srlx    %o0, 16, %g2
+
+       movrz   %g1, %g2, %o0           /* 5  */
+       clr     %o3
+
+       movrz   %g1, 16, %o2            /* 6  */
+       clr     %o4
+
+       and     %o0, 0xff, %g1          /* 7  */
+       srlx    %o0, 8, %g2
+
+       movrz   %g1, %g2, %o0           /* 8  */
+       clr     %o5
+
+       movrz   %g1, 8, %o3             /* 9  */
+       add     %o2, %o1, %o2
+
+       and     %o0, 0xf, %g1           /* 10 */
+       srlx    %o0, 4, %g2
+
+       movrz   %g1, %g2, %o0           /* 11 */
+       add     %o2, %o3, %o2
+
+       movrz   %g1, 4, %o4             /* 12 */
+
+       and     %o0, 0x3, %g1           /* 13 */
+       srlx    %o0, 2, %g2
+
+       movrz   %g1, %g2, %o0           /* 14 */
+       add     %o2, %o4, %o2
+
+       movrz   %g1, 2, %o5             /* 15 */
+
+       and     %o0, 0x1, %g1           /* 16 */
+
+       add     %o2, %o5, %o2           /* 17 */
+       xor     %g1, 0x1, %g1
+
+       retl                            /* 18 */
+        add    %o2, %g1, %o0
+ENDPROC(ffs)
+ENDPROC(__ffs)
+
+       .section        .popc_6insn_patch, "ax"
+       .word           ffs
+       brz,pn  %o0, 98f
+        neg    %o0, %g1
+       xnor    %o0, %g1, %o1
+       popc    %o1, %o0
+98:    retl
+        nop
+       .word           __ffs
+       neg     %o0, %g1
+       xnor    %o0, %g1, %o1
+       popc    %o1, %o0
+       retl
+        sub    %o0, 1, %o0
+       nop
+       .previous
diff --git a/arch/sparc/lib/hweight.S b/arch/sparc/lib/hweight.S
new file mode 100644 (file)
index 0000000..95414e0
--- /dev/null
@@ -0,0 +1,51 @@
+#include <linux/linkage.h>
+
+       .text
+       .align  32
+ENTRY(__arch_hweight8)
+       ba,pt   %xcc, __sw_hweight8
+        nop
+       nop
+ENDPROC(__arch_hweight8)
+       .section        .popc_3insn_patch, "ax"
+       .word           __arch_hweight8
+       sllx            %o0, 64-8, %g1
+       retl
+        popc           %g1, %o0
+       .previous
+
+ENTRY(__arch_hweight16)
+       ba,pt   %xcc, __sw_hweight16
+        nop
+       nop
+ENDPROC(__arch_hweight16)
+       .section        .popc_3insn_patch, "ax"
+       .word           __arch_hweight16
+       sllx            %o0, 64-16, %g1
+       retl
+        popc           %g1, %o0
+       .previous
+
+ENTRY(__arch_hweight32)
+       ba,pt   %xcc, __sw_hweight32
+        nop
+       nop
+ENDPROC(__arch_hweight32)
+       .section        .popc_3insn_patch, "ax"
+       .word           __arch_hweight32
+       sllx            %o0, 64-32, %g1
+       retl
+        popc           %g1, %o0
+       .previous
+
+ENTRY(__arch_hweight64)
+       ba,pt   %xcc, __sw_hweight64
+        nop
+       nop
+ENDPROC(__arch_hweight64)
+       .section        .popc_3insn_patch, "ax"
+       .word           __arch_hweight64
+       retl
+        popc           %o0, %o0
+       nop
+       .previous
index 3fd8e18bed80a4ea06b960572b664bfed1d23a23..adfac23d976af73a9153ce0bec648d6af5f86473 100644 (file)
@@ -1597,6 +1597,42 @@ static void __init tsb_phys_patch(void)
 static struct hv_tsb_descr ktsb_descr[NUM_KTSB_DESCR];
 extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
 
+static void patch_one_ktsb_phys(unsigned int *start, unsigned int *end, unsigned long pa)
+{
+       pa >>= KTSB_PHYS_SHIFT;
+
+       while (start < end) {
+               unsigned int *ia = (unsigned int *)(unsigned long)*start;
+
+               ia[0] = (ia[0] & ~0x3fffff) | (pa >> 10);
+               __asm__ __volatile__("flush     %0" : : "r" (ia));
+
+               ia[1] = (ia[1] & ~0x3ff) | (pa & 0x3ff);
+               __asm__ __volatile__("flush     %0" : : "r" (ia + 1));
+
+               start++;
+       }
+}
+
+static void ktsb_phys_patch(void)
+{
+       extern unsigned int __swapper_tsb_phys_patch;
+       extern unsigned int __swapper_tsb_phys_patch_end;
+       extern unsigned int __swapper_4m_tsb_phys_patch;
+       extern unsigned int __swapper_4m_tsb_phys_patch_end;
+       unsigned long ktsb_pa;
+
+       ktsb_pa = kern_base + ((unsigned long)&swapper_tsb[0] - KERNBASE);
+       patch_one_ktsb_phys(&__swapper_tsb_phys_patch,
+                           &__swapper_tsb_phys_patch_end, ktsb_pa);
+#ifndef CONFIG_DEBUG_PAGEALLOC
+       ktsb_pa = (kern_base +
+                  ((unsigned long)&swapper_4m_tsb[0] - KERNBASE));
+       patch_one_ktsb_phys(&__swapper_4m_tsb_phys_patch,
+                           &__swapper_4m_tsb_phys_patch_end, ktsb_pa);
+#endif
+}
+
 static void __init sun4v_ktsb_init(void)
 {
        unsigned long ktsb_pa;
@@ -1716,8 +1752,10 @@ void __init paging_init(void)
                sun4u_pgprot_init();
 
        if (tlb_type == cheetah_plus ||
-           tlb_type == hypervisor)
+           tlb_type == hypervisor) {
                tsb_phys_patch();
+               ktsb_phys_patch();
+       }
 
        if (tlb_type == hypervisor) {
                sun4v_patch_tlb_handlers();
index 0249b8b4db545bdb26334d05d3e105a387b6f1d0..b30f71ac0d0657f24d6a37f13c7ad802d9f36708 100644 (file)
@@ -12,6 +12,7 @@ config TILE
        select GENERIC_PENDING_IRQ if SMP
        select GENERIC_IRQ_SHOW
        select SYS_HYPERVISOR
+       select ARCH_HAVE_NMI_SAFE_CMPXCHG if !M386
 
 # FIXME: investigate whether we need/want these options.
 #      select HAVE_IOREMAP_PROT
index 849ab2fa1f5c854ef6fbfc976908ca493c352370..aec60dc0600710eeb75d8b4e32aaa1833e04eda7 100644 (file)
@@ -2,3 +2,41 @@ include include/asm-generic/Kbuild.asm
 
 header-y += ucontext.h
 header-y += hardwall.h
+
+generic-y += bug.h
+generic-y += bugs.h
+generic-y += cputime.h
+generic-y += device.h
+generic-y += div64.h
+generic-y += emergency-restart.h
+generic-y += errno.h
+generic-y += fb.h
+generic-y += fcntl.h
+generic-y += ioctl.h
+generic-y += ioctls.h
+generic-y += ipc.h
+generic-y += ipcbuf.h
+generic-y += irq_regs.h
+generic-y += kdebug.h
+generic-y += local.h
+generic-y += module.h
+generic-y += msgbuf.h
+generic-y += mutex.h
+generic-y += param.h
+generic-y += parport.h
+generic-y += poll.h
+generic-y += posix_types.h
+generic-y += resource.h
+generic-y += scatterlist.h
+generic-y += sembuf.h
+generic-y += serial.h
+generic-y += shmbuf.h
+generic-y += shmparam.h
+generic-y += socket.h
+generic-y += sockios.h
+generic-y += statfs.h
+generic-y += termbits.h
+generic-y += termios.h
+generic-y += types.h
+generic-y += ucontext.h
+generic-y += xor.h
diff --git a/arch/tile/include/asm/bug.h b/arch/tile/include/asm/bug.h
deleted file mode 100644 (file)
index b12fd89..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/bug.h>
diff --git a/arch/tile/include/asm/bugs.h b/arch/tile/include/asm/bugs.h
deleted file mode 100644 (file)
index 61791e1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/bugs.h>
diff --git a/arch/tile/include/asm/cputime.h b/arch/tile/include/asm/cputime.h
deleted file mode 100644 (file)
index 6d68ad7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/cputime.h>
diff --git a/arch/tile/include/asm/device.h b/arch/tile/include/asm/device.h
deleted file mode 100644 (file)
index f0a4c25..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/device.h>
diff --git a/arch/tile/include/asm/div64.h b/arch/tile/include/asm/div64.h
deleted file mode 100644 (file)
index 6cd978c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/div64.h>
diff --git a/arch/tile/include/asm/emergency-restart.h b/arch/tile/include/asm/emergency-restart.h
deleted file mode 100644 (file)
index 3711bd9..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/emergency-restart.h>
diff --git a/arch/tile/include/asm/errno.h b/arch/tile/include/asm/errno.h
deleted file mode 100644 (file)
index 4c82b50..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/errno.h>
diff --git a/arch/tile/include/asm/fb.h b/arch/tile/include/asm/fb.h
deleted file mode 100644 (file)
index 3a4988e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/fb.h>
diff --git a/arch/tile/include/asm/fcntl.h b/arch/tile/include/asm/fcntl.h
deleted file mode 100644 (file)
index 46ab12d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/fcntl.h>
index 51537ff9265acb240c08e7672f5b1d0a18431621..c66f7933beaa0952c31601c17559a7eaa83206c2 100644 (file)
@@ -75,12 +75,6 @@ extern void __set_fixmap(enum fixed_addresses idx,
 
 #define set_fixmap(idx, phys) \
                __set_fixmap(idx, phys, PAGE_KERNEL)
-/*
- * Some hardware wants to get fixmapped without caching.
- */
-#define set_fixmap_nocache(idx, phys) \
-               __set_fixmap(idx, phys, PAGE_KERNEL_NOCACHE)
-
 #define clear_fixmap(idx) \
                __set_fixmap(idx, 0, __pgprot(0))
 
diff --git a/arch/tile/include/asm/ioctl.h b/arch/tile/include/asm/ioctl.h
deleted file mode 100644 (file)
index b279fe0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ioctl.h>
diff --git a/arch/tile/include/asm/ioctls.h b/arch/tile/include/asm/ioctls.h
deleted file mode 100644 (file)
index ec34c76..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ioctls.h>
diff --git a/arch/tile/include/asm/ipc.h b/arch/tile/include/asm/ipc.h
deleted file mode 100644 (file)
index a46e3d9..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ipc.h>
diff --git a/arch/tile/include/asm/ipcbuf.h b/arch/tile/include/asm/ipcbuf.h
deleted file mode 100644 (file)
index 84c7e51..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ipcbuf.h>
diff --git a/arch/tile/include/asm/irq_regs.h b/arch/tile/include/asm/irq_regs.h
deleted file mode 100644 (file)
index 3dd9c0b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/irq_regs.h>
diff --git a/arch/tile/include/asm/kdebug.h b/arch/tile/include/asm/kdebug.h
deleted file mode 100644 (file)
index 6ece1b0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/kdebug.h>
diff --git a/arch/tile/include/asm/local.h b/arch/tile/include/asm/local.h
deleted file mode 100644 (file)
index c11c530..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/local.h>
diff --git a/arch/tile/include/asm/module.h b/arch/tile/include/asm/module.h
deleted file mode 100644 (file)
index 1e4b79f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/module.h>
diff --git a/arch/tile/include/asm/msgbuf.h b/arch/tile/include/asm/msgbuf.h
deleted file mode 100644 (file)
index 809134c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/msgbuf.h>
diff --git a/arch/tile/include/asm/mutex.h b/arch/tile/include/asm/mutex.h
deleted file mode 100644 (file)
index ff6101a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/mutex-dec.h>
diff --git a/arch/tile/include/asm/param.h b/arch/tile/include/asm/param.h
deleted file mode 100644 (file)
index 965d454..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/param.h>
diff --git a/arch/tile/include/asm/parport.h b/arch/tile/include/asm/parport.h
deleted file mode 100644 (file)
index cf252af..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/parport.h>
diff --git a/arch/tile/include/asm/poll.h b/arch/tile/include/asm/poll.h
deleted file mode 100644 (file)
index c98509d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/poll.h>
diff --git a/arch/tile/include/asm/posix_types.h b/arch/tile/include/asm/posix_types.h
deleted file mode 100644 (file)
index 22cae62..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/posix_types.h>
diff --git a/arch/tile/include/asm/resource.h b/arch/tile/include/asm/resource.h
deleted file mode 100644 (file)
index 04bc4db..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/resource.h>
diff --git a/arch/tile/include/asm/scatterlist.h b/arch/tile/include/asm/scatterlist.h
deleted file mode 100644 (file)
index 35d786f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/scatterlist.h>
diff --git a/arch/tile/include/asm/sembuf.h b/arch/tile/include/asm/sembuf.h
deleted file mode 100644 (file)
index 7673b83..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/sembuf.h>
diff --git a/arch/tile/include/asm/serial.h b/arch/tile/include/asm/serial.h
deleted file mode 100644 (file)
index a0cb0ca..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/serial.h>
diff --git a/arch/tile/include/asm/shmbuf.h b/arch/tile/include/asm/shmbuf.h
deleted file mode 100644 (file)
index 83c05fc..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/shmbuf.h>
diff --git a/arch/tile/include/asm/shmparam.h b/arch/tile/include/asm/shmparam.h
deleted file mode 100644 (file)
index 93f30de..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/shmparam.h>
diff --git a/arch/tile/include/asm/socket.h b/arch/tile/include/asm/socket.h
deleted file mode 100644 (file)
index 6b71384..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/socket.h>
diff --git a/arch/tile/include/asm/sockios.h b/arch/tile/include/asm/sockios.h
deleted file mode 100644 (file)
index def6d47..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/sockios.h>
diff --git a/arch/tile/include/asm/statfs.h b/arch/tile/include/asm/statfs.h
deleted file mode 100644 (file)
index 0b91fe1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/statfs.h>
diff --git a/arch/tile/include/asm/termbits.h b/arch/tile/include/asm/termbits.h
deleted file mode 100644 (file)
index 3935b10..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/termbits.h>
diff --git a/arch/tile/include/asm/termios.h b/arch/tile/include/asm/termios.h
deleted file mode 100644 (file)
index 280d78a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/termios.h>
diff --git a/arch/tile/include/asm/types.h b/arch/tile/include/asm/types.h
deleted file mode 100644 (file)
index b9e79bc..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/types.h>
diff --git a/arch/tile/include/asm/ucontext.h b/arch/tile/include/asm/ucontext.h
deleted file mode 100644 (file)
index 9bc07b9..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ucontext.h>
diff --git a/arch/tile/include/asm/xor.h b/arch/tile/include/asm/xor.h
deleted file mode 100644 (file)
index c82eb12..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/xor.h>
diff --git a/arch/tile/include/hv/drv_srom_intf.h b/arch/tile/include/hv/drv_srom_intf.h
new file mode 100644 (file)
index 0000000..6395faa
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011 Tilera Corporation. 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, version 2.
+ *
+ *   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.
+ */
+
+/**
+ * @file drv_srom_intf.h
+ * Interface definitions for the SPI Flash ROM driver.
+ */
+
+#ifndef _SYS_HV_INCLUDE_DRV_SROM_INTF_H
+#define _SYS_HV_INCLUDE_DRV_SROM_INTF_H
+
+/** Read this offset to get the total device size. */
+#define SROM_TOTAL_SIZE_OFF   0xF0000000
+
+/** Read this offset to get the device sector size. */
+#define SROM_SECTOR_SIZE_OFF  0xF0000004
+
+/** Read this offset to get the device page size. */
+#define SROM_PAGE_SIZE_OFF    0xF0000008
+
+/** Write this offset to flush any pending writes. */
+#define SROM_FLUSH_OFF        0xF1000000
+
+/** Write this offset, plus the byte offset of the start of a sector, to
+ *  erase a sector.  Any write data is ignored, but there must be at least
+ *  one byte of write data.  Only applies when the driver is in MTD mode.
+ */
+#define SROM_ERASE_OFF        0xF2000000
+
+#endif /* _SYS_HV_INCLUDE_DRV_SROM_INTF_H */
index c4be58cc5d5083fe213c9994b236c272f13c992a..f6f50f2a5e37bea0da28c17af51d622498dd6322 100644 (file)
@@ -78,7 +78,6 @@ static struct clocksource cycle_counter_cs = {
        .rating = 300,
        .read = clocksource_get_cycles,
        .mask = CLOCKSOURCE_MASK(64),
-       .shift = 22,   /* typical value, e.g. x86 tsc uses this */
        .flags = CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
@@ -91,8 +90,6 @@ void __init setup_clock(void)
        cycles_per_sec = hv_sysconf(HV_SYSCONF_CPU_SPEED);
        sched_clock_mult =
                clocksource_hz2mult(cycles_per_sec, SCHED_CLOCK_SHIFT);
-       cycle_counter_cs.mult =
-               clocksource_hz2mult(cycles_per_sec, cycle_counter_cs.shift);
 }
 
 void __init calibrate_delay(void)
@@ -107,7 +104,7 @@ void __init calibrate_delay(void)
 void __init time_init(void)
 {
        /* Initialize and register the clock source. */
-       clocksource_register(&cycle_counter_cs);
+       clocksource_register_hz(&cycle_counter_cs, cycles_per_sec);
 
        /* Start up the tile-timer interrupt source on the boot cpu. */
        setup_tile_timer();
index 4e10c4023028181f9700143ab29738b8e0a10a84..7309988c9794c82f195572afd2b63658a4431cb4 100644 (file)
@@ -836,8 +836,7 @@ void __init mem_init(void)
 #endif
 
 #ifdef CONFIG_FLATMEM
-       if (!mem_map)
-               BUG();
+       BUG_ON(!mem_map);
 #endif
 
 #ifdef CONFIG_HIGHMEM
index 7cf916fc1ce7e4920204a184a15b6e5ef2cfd1a4..6a47bb22657fd3d55835c32b2e834cd42e3ad28c 100644 (file)
@@ -72,6 +72,7 @@ config X86
        select USE_GENERIC_SMP_HELPERS if SMP
        select HAVE_BPF_JIT if (X86_64 && NET)
        select CLKEVT_I8253
+       select ARCH_HAVE_NMI_SAFE_CMPXCHG
 
 config INSTRUCTION_DECODER
        def_bool (KPROBES || PERF_EVENTS)
index d02804d650c4596aa4644a8fccb75ba72d387548..d8e8eefbe24c9a45edfeed4da491cc3b903a2312 100644 (file)
@@ -40,8 +40,6 @@
 #include <linux/compiler.h>
 #include <asm/page.h>
 
-#include <xen/xen.h>
-
 #define build_mmio_read(name, size, type, reg, barrier) \
 static inline type name(const volatile void __iomem *addr) \
 { type ret; asm volatile("mov" size " %1,%0":reg (ret) \
@@ -334,6 +332,7 @@ extern void fixup_early_ioremap(void);
 extern bool is_early_ioremap_ptep(pte_t *ptep);
 
 #ifdef CONFIG_XEN
+#include <xen/xen.h>
 struct bio_vec;
 
 extern bool xen_biovec_phys_mergeable(const struct bio_vec *vec1,
index 219371546afd343ddeb39a1f3018ce0e5c6cfc31..0d1171c9772937f4ec6980527aaebfdb9b6533cf 100644 (file)
@@ -751,8 +751,6 @@ static inline void __sti_mwait(unsigned long eax, unsigned long ecx)
                     :: "a" (eax), "c" (ecx));
 }
 
-extern void mwait_idle_with_hints(unsigned long eax, unsigned long ecx);
-
 extern void select_idle_routine(const struct cpuinfo_x86 *c);
 extern void init_amd_e400_c1e_mask(void);
 
index 5812404a0d4ce5e3eb815675f591f18b95762329..f50e7fb2a201b752c5c007dc9da41397ca9c6c46 100644 (file)
@@ -149,6 +149,29 @@ int acpi_processor_ffh_cstate_probe(unsigned int cpu,
 }
 EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_probe);
 
+/*
+ * This uses new MONITOR/MWAIT instructions on P4 processors with PNI,
+ * which can obviate IPI to trigger checking of need_resched.
+ * We execute MONITOR against need_resched and enter optimized wait state
+ * through MWAIT. Whenever someone changes need_resched, we would be woken
+ * up from MWAIT (without an IPI).
+ *
+ * New with Core Duo processors, MWAIT can take some hints based on CPU
+ * capability.
+ */
+void mwait_idle_with_hints(unsigned long ax, unsigned long cx)
+{
+       if (!need_resched()) {
+               if (this_cpu_has(X86_FEATURE_CLFLUSH_MONITOR))
+                       clflush((void *)&current_thread_info()->flags);
+
+               __monitor((void *)&current_thread_info()->flags, 0, 0);
+               smp_mb();
+               if (!need_resched())
+                       __mwait(ax, cx);
+       }
+}
+
 void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx)
 {
        unsigned int cpu = smp_processor_id();
index e1ba8cb24e4edb6629a9107312c071739c32de75..e7e3b019c4393b1c7a1dd78eef02decb89202c72 100644 (file)
@@ -438,29 +438,6 @@ void cpu_idle_wait(void)
 }
 EXPORT_SYMBOL_GPL(cpu_idle_wait);
 
-/*
- * This uses new MONITOR/MWAIT instructions on P4 processors with PNI,
- * which can obviate IPI to trigger checking of need_resched.
- * We execute MONITOR against need_resched and enter optimized wait state
- * through MWAIT. Whenever someone changes need_resched, we would be woken
- * up from MWAIT (without an IPI).
- *
- * New with Core Duo processors, MWAIT can take some hints based on CPU
- * capability.
- */
-void mwait_idle_with_hints(unsigned long ax, unsigned long cx)
-{
-       if (!need_resched()) {
-               if (this_cpu_has(X86_FEATURE_CLFLUSH_MONITOR))
-                       clflush((void *)&current_thread_info()->flags);
-
-               __monitor((void *)&current_thread_info()->flags, 0, 0);
-               smp_mb();
-               if (!need_resched())
-                       __mwait(ax, cx);
-       }
-}
-
 /* Default MONITOR/MWAIT with no hints, used for default C1 state */
 static void mwait_idle(void)
 {
index a3d0dc59067be542d7423d2a8abbc99801c0d3f6..7a3b65107a27cce70ff2b20c03e909c712c32e7e 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/uaccess.h>
 #include <linux/io.h>
 #include <linux/kdebug.h>
+#include <linux/cpuidle.h>
 
 #include <asm/pgtable.h>
 #include <asm/system.h>
@@ -109,7 +110,8 @@ void cpu_idle(void)
                        local_irq_disable();
                        /* Don't trace irqs off for idle */
                        stop_critical_timings();
-                       pm_idle();
+                       if (cpuidle_idle_call())
+                               pm_idle();
                        start_critical_timings();
                }
                tick_nohz_restart_sched_tick();
index ca6f7ab8df332992166d51c802216d350c8729cc..f693e44e1bf63a303a3e53a00d927721e6602e86 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/uaccess.h>
 #include <linux/io.h>
 #include <linux/ftrace.h>
+#include <linux/cpuidle.h>
 
 #include <asm/pgtable.h>
 #include <asm/system.h>
@@ -136,7 +137,8 @@ void cpu_idle(void)
                        enter_idle();
                        /* Don't trace irqs off for idle */
                        stop_critical_timings();
-                       pm_idle();
+                       if (cpuidle_idle_call())
+                               pm_idle();
                        start_critical_timings();
 
                        /* In many cases the interrupt that ended idle
index f61ccdd4934141444f1c8f27d2b3a783f06c37d0..1ea38775a6d32aadc23744a8a288b295eaaedf7c 100644 (file)
@@ -1,3 +1,4 @@
 obj-$(CONFIG_X86_MRST)         += mrst.o
 obj-$(CONFIG_X86_MRST)         += vrtc.o
 obj-$(CONFIG_EARLY_PRINTK_MRST)        += early_printk_mrst.o
+obj-$(CONFIG_X86_MRST)         += pmu.o
diff --git a/arch/x86/platform/mrst/pmu.c b/arch/x86/platform/mrst/pmu.c
new file mode 100644 (file)
index 0000000..9281da7
--- /dev/null
@@ -0,0 +1,817 @@
+/*
+ * mrst/pmu.c - driver for MRST Power Management Unit
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/cpuidle.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/seq_file.h>
+#include <linux/sfi.h>
+#include <asm/intel_scu_ipc.h>
+#include "pmu.h"
+
+#define IPCMSG_FW_REVISION     0xF4
+
+struct mrst_device {
+       u16 pci_dev_num;        /* DEBUG only */
+       u16 lss;
+       u16 latest_request;
+       unsigned int pci_state_counts[PCI_D3cold + 1]; /* DEBUG only */
+};
+
+/*
+ * comlete list of MRST PCI devices
+ */
+static struct mrst_device mrst_devs[] = {
+/*  0 */ { 0x0800, LSS_SPI0 },         /* Moorestown SPI Ctrl 0 */
+/*  1 */ { 0x0801, LSS_SPI1 },         /* Moorestown SPI Ctrl 1 */
+/*  2 */ { 0x0802, LSS_I2C0 },         /* Moorestown I2C 0 */
+/*  3 */ { 0x0803, LSS_I2C1 },         /* Moorestown I2C 1 */
+/*  4 */ { 0x0804, LSS_I2C2 },         /* Moorestown I2C 2 */
+/*  5 */ { 0x0805, LSS_KBD },          /* Moorestown Keyboard Ctrl */
+/*  6 */ { 0x0806, LSS_USB_HC },       /* Moorestown USB Ctrl */
+/*  7 */ { 0x0807, LSS_SD_HC0 },       /* Moorestown SD Host Ctrl 0 */
+/*  8 */ { 0x0808, LSS_SD_HC1 },       /* Moorestown SD Host Ctrl 1 */
+/*  9 */ { 0x0809, LSS_NAND },         /* Moorestown NAND Ctrl */
+/* 10 */ { 0x080a, LSS_AUDIO },                /* Moorestown Audio Ctrl */
+/* 11 */ { 0x080b, LSS_IMAGING },      /* Moorestown ISP */
+/* 12 */ { 0x080c, LSS_SECURITY },     /* Moorestown Security Controller */
+/* 13 */ { 0x080d, LSS_DISPLAY },      /* Moorestown External Displays */
+/* 14 */ { 0x080e, 0 },                        /* Moorestown SCU IPC */
+/* 15 */ { 0x080f, LSS_GPIO },         /* Moorestown GPIO Controller */
+/* 16 */ { 0x0810, 0 },                        /* Moorestown Power Management Unit */
+/* 17 */ { 0x0811, LSS_USB_OTG },      /* Moorestown OTG Ctrl */
+/* 18 */ { 0x0812, LSS_SPI2 },         /* Moorestown SPI Ctrl 2 */
+/* 19 */ { 0x0813, 0 },                        /* Moorestown SC DMA */
+/* 20 */ { 0x0814, LSS_AUDIO_LPE },    /* Moorestown LPE DMA */
+/* 21 */ { 0x0815, LSS_AUDIO_SSP },    /* Moorestown SSP0 */
+
+/* 22 */ { 0x084F, LSS_SD_HC2 },       /* Moorestown SD Host Ctrl 2 */
+
+/* 23 */ { 0x4102, 0 },                        /* Lincroft */
+/* 24 */ { 0x4110, 0 },                        /* Lincroft */
+};
+
+/* n.b. We ignore PCI-id 0x815 in LSS9 b/c MeeGo has no driver for it */
+static u16 mrst_lss9_pci_ids[] = {0x080a, 0x0814, 0};
+static u16 mrst_lss10_pci_ids[] = {0x0800, 0x0801, 0x0802, 0x0803,
+                                       0x0804, 0x0805, 0x080f, 0};
+
+/* handle concurrent SMP invokations of pmu_pci_set_power_state() */
+static spinlock_t mrst_pmu_power_state_lock;
+
+static unsigned int wake_counters[MRST_NUM_LSS];       /* DEBUG only */
+static unsigned int pmu_irq_stats[INT_INVALID + 1];    /* DEBUG only */
+
+static int graphics_is_off;
+static int lss_s0i3_enabled;
+static bool mrst_pmu_s0i3_enable;
+
+/*  debug counters */
+static u32 pmu_wait_ready_calls;
+static u32 pmu_wait_ready_udelays;
+static u32 pmu_wait_ready_udelays_max;
+static u32 pmu_wait_done_calls;
+static u32 pmu_wait_done_udelays;
+static u32 pmu_wait_done_udelays_max;
+static u32 pmu_set_power_state_entry;
+static u32 pmu_set_power_state_send_cmd;
+
+static struct mrst_device *pci_id_2_mrst_dev(u16 pci_dev_num)
+{
+       int index = 0;
+
+       if ((pci_dev_num >= 0x0800) && (pci_dev_num <= 0x815))
+               index = pci_dev_num - 0x800;
+       else if (pci_dev_num == 0x084F)
+               index = 22;
+       else if (pci_dev_num == 0x4102)
+               index = 23;
+       else if (pci_dev_num == 0x4110)
+               index = 24;
+
+       if (pci_dev_num != mrst_devs[index].pci_dev_num) {
+               WARN_ONCE(1, FW_BUG "Unknown PCI device 0x%04X\n", pci_dev_num);
+               return 0;
+       }
+
+       return &mrst_devs[index];
+}
+
+/**
+ * mrst_pmu_validate_cstates
+ * @dev: cpuidle_device
+ *
+ * Certain states are not appropriate for governor to pick in some cases.
+ * This function will be called as cpuidle_device's prepare callback and
+ * thus tells governor to ignore such states when selecting the next state
+ * to enter.
+ */
+
+#define IDLE_STATE4_IS_C6      4
+#define IDLE_STATE5_IS_S0I3    5
+
+int mrst_pmu_invalid_cstates(void)
+{
+       int cpu = smp_processor_id();
+
+       /*
+        * Demote to C4 if the PMU is busy.
+        * Since LSS changes leave the busy bit clear...
+        * busy means either the PMU is waiting for an ACK-C6 that
+        * isn't coming due to an MWAIT that returned immediately;
+        * or we returned from S0i3 successfully, and the PMU
+        * is not done sending us interrupts.
+        */
+       if (pmu_read_busy_status())
+               return 1 << IDLE_STATE4_IS_C6 | 1 << IDLE_STATE5_IS_S0I3;
+
+       /*
+        * Disallow S0i3 if: PMU is not initialized, or CPU1 is active,
+        * or if device LSS is insufficient, or the GPU is active,
+        * or if it has been explicitly disabled.
+        */
+       if (!pmu_reg || !cpumask_equal(cpu_online_mask, cpumask_of(cpu)) ||
+           !lss_s0i3_enabled || !graphics_is_off || !mrst_pmu_s0i3_enable)
+               return 1 << IDLE_STATE5_IS_S0I3;
+       else
+               return 0;
+}
+
+/*
+ * pmu_update_wake_counters(): read PM_WKS, update wake_counters[]
+ * DEBUG only.
+ */
+static void pmu_update_wake_counters(void)
+{
+       int lss;
+       u32 wake_status;
+
+       wake_status = pmu_read_wks();
+
+       for (lss = 0; lss < MRST_NUM_LSS; ++lss) {
+               if (wake_status & (1 << lss))
+                       wake_counters[lss]++;
+       }
+}
+
+int mrst_pmu_s0i3_entry(void)
+{
+       int status;
+
+       /* Clear any possible error conditions */
+       pmu_write_ics(0x300);
+
+       /* set wake control to current D-states */
+       pmu_write_wssc(S0I3_SSS_TARGET);
+
+       status = mrst_s0i3_entry(PM_S0I3_COMMAND, &pmu_reg->pm_cmd);
+       pmu_update_wake_counters();
+       return status;
+}
+
+/* poll for maximum of 5ms for busy bit to clear */
+static int pmu_wait_ready(void)
+{
+       int udelays;
+
+       pmu_wait_ready_calls++;
+
+       for (udelays = 0; udelays < 500; ++udelays) {
+               if (udelays > pmu_wait_ready_udelays_max)
+                       pmu_wait_ready_udelays_max = udelays;
+
+               if (pmu_read_busy_status() == 0)
+                       return 0;
+
+               udelay(10);
+               pmu_wait_ready_udelays++;
+       }
+
+       /*
+        * if this fires, observe
+        * /sys/kernel/debug/mrst_pmu_wait_ready_calls
+        * /sys/kernel/debug/mrst_pmu_wait_ready_udelays
+        */
+       WARN_ONCE(1, "SCU not ready for 5ms");
+       return -EBUSY;
+}
+/* poll for maximum of 50ms us for busy bit to clear */
+static int pmu_wait_done(void)
+{
+       int udelays;
+
+       pmu_wait_done_calls++;
+
+       for (udelays = 0; udelays < 500; ++udelays) {
+               if (udelays > pmu_wait_done_udelays_max)
+                       pmu_wait_done_udelays_max = udelays;
+
+               if (pmu_read_busy_status() == 0)
+                       return 0;
+
+               udelay(100);
+               pmu_wait_done_udelays++;
+       }
+
+       /*
+        * if this fires, observe
+        * /sys/kernel/debug/mrst_pmu_wait_done_calls
+        * /sys/kernel/debug/mrst_pmu_wait_done_udelays
+        */
+       WARN_ONCE(1, "SCU not done for 50ms");
+       return -EBUSY;
+}
+
+u32 mrst_pmu_msi_is_disabled(void)
+{
+       return pmu_msi_is_disabled();
+}
+
+void mrst_pmu_enable_msi(void)
+{
+       pmu_msi_enable();
+}
+
+/**
+ * pmu_irq - pmu driver interrupt handler
+ * Context: interrupt context
+ */
+static irqreturn_t pmu_irq(int irq, void *dummy)
+{
+       union pmu_pm_ics pmu_ics;
+
+       pmu_ics.value = pmu_read_ics();
+
+       if (!pmu_ics.bits.pending)
+               return IRQ_NONE;
+
+       switch (pmu_ics.bits.cause) {
+       case INT_SPURIOUS:
+       case INT_CMD_DONE:
+       case INT_CMD_ERR:
+       case INT_WAKE_RX:
+       case INT_SS_ERROR:
+       case INT_S0IX_MISS:
+       case INT_NO_ACKC6:
+               pmu_irq_stats[pmu_ics.bits.cause]++;
+               break;
+       default:
+               pmu_irq_stats[INT_INVALID]++;
+       }
+
+       pmu_write_ics(pmu_ics.value); /* Clear pending interrupt */
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Translate PCI power management to MRST LSS D-states
+ */
+static int pci_2_mrst_state(int lss, pci_power_t pci_state)
+{
+       switch (pci_state) {
+       case PCI_D0:
+               if (SSMSK(D0i1, lss) & D0I1_ACG_SSS_TARGET)
+                       return D0i1;
+               else
+                       return D0;
+       case PCI_D1:
+               return D0i1;
+       case PCI_D2:
+               return D0i2;
+       case PCI_D3hot:
+       case PCI_D3cold:
+               return D0i3;
+       default:
+               WARN(1, "pci_state %d\n", pci_state);
+               return 0;
+       }
+}
+
+static int pmu_issue_command(u32 pm_ssc)
+{
+       union pmu_pm_set_cfg_cmd_t command;
+
+       if (pmu_read_busy_status()) {
+               pr_debug("pmu is busy, Operation not permitted\n");
+               return -1;
+       }
+
+       /*
+        * enable interrupts in PMU so that interrupts are
+        * propagated when ioc bit for a particular set
+        * command is set
+        */
+
+       pmu_irq_enable();
+
+       /* Configure the sub systems for pmu2 */
+
+       pmu_write_ssc(pm_ssc);
+
+       /*
+        * Send the set config command for pmu its configured
+        * for mode CM_IMMEDIATE & hence with No Trigger
+        */
+
+       command.pmu2_params.d_param.cfg_mode = CM_IMMEDIATE;
+       command.pmu2_params.d_param.cfg_delay = 0;
+       command.pmu2_params.d_param.rsvd = 0;
+
+       /* construct the command to send SET_CFG to particular PMU */
+       command.pmu2_params.d_param.cmd = SET_CFG_CMD;
+       command.pmu2_params.d_param.ioc = 0;
+       command.pmu2_params.d_param.mode_id = 0;
+       command.pmu2_params.d_param.sys_state = SYS_STATE_S0I0;
+
+       /* write the value of PM_CMD into particular PMU */
+       pr_debug("pmu command being written %x\n",
+                       command.pmu_pm_set_cfg_cmd_value);
+
+       pmu_write_cmd(command.pmu_pm_set_cfg_cmd_value);
+
+       return 0;
+}
+
+static u16 pmu_min_lss_pci_req(u16 *ids, u16 pci_state)
+{
+       u16 existing_request;
+       int i;
+
+       for (i = 0; ids[i]; ++i) {
+               struct mrst_device *mrst_dev;
+
+               mrst_dev = pci_id_2_mrst_dev(ids[i]);
+               if (unlikely(!mrst_dev))
+                       continue;
+
+               existing_request = mrst_dev->latest_request;
+               if (existing_request < pci_state)
+                       pci_state = existing_request;
+       }
+       return pci_state;
+}
+
+/**
+ * pmu_pci_set_power_state - Callback function is used by all the PCI devices
+ *                     for a platform  specific device power on/shutdown.
+ */
+
+int pmu_pci_set_power_state(struct pci_dev *pdev, pci_power_t pci_state)
+{
+       u32 old_sss, new_sss;
+       int status = 0;
+       struct mrst_device *mrst_dev;
+
+       pmu_set_power_state_entry++;
+
+       BUG_ON(pdev->vendor != PCI_VENDOR_ID_INTEL);
+       BUG_ON(pci_state < PCI_D0 || pci_state > PCI_D3cold);
+
+       mrst_dev = pci_id_2_mrst_dev(pdev->device);
+       if (unlikely(!mrst_dev))
+               return -ENODEV;
+
+       mrst_dev->pci_state_counts[pci_state]++;        /* count invocations */
+
+       /* PMU driver calls self as part of PCI initialization, ignore */
+       if (pdev->device == PCI_DEV_ID_MRST_PMU)
+               return 0;
+
+       BUG_ON(!pmu_reg); /* SW bug if called before initialized */
+
+       spin_lock(&mrst_pmu_power_state_lock);
+
+       if (pdev->d3_delay) {
+               dev_dbg(&pdev->dev, "d3_delay %d, should be 0\n",
+                       pdev->d3_delay);
+               pdev->d3_delay = 0;
+       }
+       /*
+        * If Lincroft graphics, simply remember state
+        */
+       if ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY
+               && !((pdev->class & PCI_SUB_CLASS_MASK) >> 8)) {
+               if (pci_state == PCI_D0)
+                       graphics_is_off = 0;
+               else
+                       graphics_is_off = 1;
+               goto ret;
+       }
+
+       if (!mrst_dev->lss)
+               goto ret;       /* device with no LSS */
+
+       if (mrst_dev->latest_request == pci_state)
+               goto ret;       /* no change */
+
+       mrst_dev->latest_request = pci_state;   /* record latest request */
+
+       /*
+        * LSS9 and LSS10 contain multiple PCI devices.
+        * Use the lowest numbered (highest power) state in the LSS
+        */
+       if (mrst_dev->lss == 9)
+               pci_state = pmu_min_lss_pci_req(mrst_lss9_pci_ids, pci_state);
+       else if (mrst_dev->lss == 10)
+               pci_state = pmu_min_lss_pci_req(mrst_lss10_pci_ids, pci_state);
+
+       status = pmu_wait_ready();
+       if (status)
+               goto ret;
+
+       old_sss = pmu_read_sss();
+       new_sss = old_sss & ~SSMSK(3, mrst_dev->lss);
+       new_sss |= SSMSK(pci_2_mrst_state(mrst_dev->lss, pci_state),
+                       mrst_dev->lss);
+
+       if (new_sss == old_sss)
+               goto ret;       /* nothing to do */
+
+       pmu_set_power_state_send_cmd++;
+
+       status = pmu_issue_command(new_sss);
+
+       if (unlikely(status != 0)) {
+               dev_err(&pdev->dev, "Failed to Issue a PM command\n");
+               goto ret;
+       }
+
+       if (pmu_wait_done())
+               goto ret;
+
+       lss_s0i3_enabled =
+       ((pmu_read_sss() & S0I3_SSS_TARGET) == S0I3_SSS_TARGET);
+ret:
+       spin_unlock(&mrst_pmu_power_state_lock);
+       return status;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static char *d0ix_names[] = {"D0", "D0i1", "D0i2", "D0i3"};
+
+static inline const char *d0ix_name(int state)
+{
+       return d0ix_names[(int) state];
+}
+
+static int debug_mrst_pmu_show(struct seq_file *s, void *unused)
+{
+       struct pci_dev *pdev = NULL;
+       u32 cur_pmsss;
+       int lss;
+
+       seq_printf(s, "0x%08X D0I1_ACG_SSS_TARGET\n", D0I1_ACG_SSS_TARGET);
+
+       cur_pmsss = pmu_read_sss();
+
+       seq_printf(s, "0x%08X S0I3_SSS_TARGET\n", S0I3_SSS_TARGET);
+
+       seq_printf(s, "0x%08X Current SSS ", cur_pmsss);
+       seq_printf(s, lss_s0i3_enabled ? "\n" : "[BLOCKS s0i3]\n");
+
+       if (cpumask_equal(cpu_online_mask, cpumask_of(0)))
+               seq_printf(s, "cpu0 is only cpu online\n");
+       else
+               seq_printf(s, "cpu0 is NOT only cpu online [BLOCKS S0i3]\n");
+
+       seq_printf(s, "GFX: %s\n", graphics_is_off ? "" : "[BLOCKS s0i3]");
+
+
+       for_each_pci_dev(pdev) {
+               int pos;
+               u16 pmcsr;
+               struct mrst_device *mrst_dev;
+               int i;
+
+               mrst_dev = pci_id_2_mrst_dev(pdev->device);
+
+               seq_printf(s, "%s %04x/%04X %-16.16s ",
+                       dev_name(&pdev->dev),
+                       pdev->vendor, pdev->device,
+                       dev_driver_string(&pdev->dev));
+
+               if (unlikely (!mrst_dev)) {
+                       seq_printf(s, " UNKNOWN\n");
+                       continue;
+               }
+
+               if (mrst_dev->lss)
+                       seq_printf(s, "LSS %2d %-4s ", mrst_dev->lss,
+                               d0ix_name(((cur_pmsss >>
+                                       (mrst_dev->lss * 2)) & 0x3)));
+               else
+                       seq_printf(s, "            ");
+
+               /* PCI PM config space setting */
+               pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
+               if (pos != 0) {
+                       pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr);
+               seq_printf(s, "PCI-%-4s",
+                       pci_power_name(pmcsr & PCI_PM_CTRL_STATE_MASK));
+               } else {
+                       seq_printf(s, "        ");
+               }
+
+               seq_printf(s, " %s ", pci_power_name(mrst_dev->latest_request));
+               for (i = 0; i <= PCI_D3cold; ++i)
+                       seq_printf(s, "%d ", mrst_dev->pci_state_counts[i]);
+
+               if (mrst_dev->lss) {
+                       unsigned int lssmask;
+
+                       lssmask = SSMSK(D0i3, mrst_dev->lss);
+
+                       if ((lssmask & S0I3_SSS_TARGET) &&
+                               ((lssmask & cur_pmsss) !=
+                                       (lssmask & S0I3_SSS_TARGET)))
+                                               seq_printf(s , "[BLOCKS s0i3]");
+               }
+
+               seq_printf(s, "\n");
+       }
+       seq_printf(s, "Wake Counters:\n");
+       for (lss = 0; lss < MRST_NUM_LSS; ++lss)
+               seq_printf(s, "LSS%d %d\n", lss, wake_counters[lss]);
+
+       seq_printf(s, "Interrupt Counters:\n");
+       seq_printf(s,
+               "INT_SPURIOUS \t%8u\n" "INT_CMD_DONE \t%8u\n"
+               "INT_CMD_ERR  \t%8u\n" "INT_WAKE_RX  \t%8u\n"
+               "INT_SS_ERROR \t%8u\n" "INT_S0IX_MISS\t%8u\n"
+               "INT_NO_ACKC6 \t%8u\n" "INT_INVALID  \t%8u\n",
+               pmu_irq_stats[INT_SPURIOUS], pmu_irq_stats[INT_CMD_DONE],
+               pmu_irq_stats[INT_CMD_ERR], pmu_irq_stats[INT_WAKE_RX],
+               pmu_irq_stats[INT_SS_ERROR], pmu_irq_stats[INT_S0IX_MISS],
+               pmu_irq_stats[INT_NO_ACKC6], pmu_irq_stats[INT_INVALID]);
+
+       seq_printf(s, "mrst_pmu_wait_ready_calls          %8d\n",
+                       pmu_wait_ready_calls);
+       seq_printf(s, "mrst_pmu_wait_ready_udelays        %8d\n",
+                       pmu_wait_ready_udelays);
+       seq_printf(s, "mrst_pmu_wait_ready_udelays_max    %8d\n",
+                       pmu_wait_ready_udelays_max);
+       seq_printf(s, "mrst_pmu_wait_done_calls           %8d\n",
+                       pmu_wait_done_calls);
+       seq_printf(s, "mrst_pmu_wait_done_udelays         %8d\n",
+                       pmu_wait_done_udelays);
+       seq_printf(s, "mrst_pmu_wait_done_udelays_max     %8d\n",
+                       pmu_wait_done_udelays_max);
+       seq_printf(s, "mrst_pmu_set_power_state_entry     %8d\n",
+                       pmu_set_power_state_entry);
+       seq_printf(s, "mrst_pmu_set_power_state_send_cmd  %8d\n",
+                       pmu_set_power_state_send_cmd);
+       seq_printf(s, "SCU busy: %d\n", pmu_read_busy_status());
+
+       return 0;
+}
+
+static int debug_mrst_pmu_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, debug_mrst_pmu_show, NULL);
+}
+
+static const struct file_operations devices_state_operations = {
+       .open           = debug_mrst_pmu_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+#endif /* DEBUG_FS */
+
+/*
+ * Validate SCU PCI shim PCI vendor capability byte
+ * against LSS hard-coded in mrst_devs[] above.
+ * DEBUG only.
+ */
+static void pmu_scu_firmware_debug(void)
+{
+       struct pci_dev *pdev = NULL;
+
+       for_each_pci_dev(pdev) {
+               struct mrst_device *mrst_dev;
+               u8 pci_config_lss;
+               int pos;
+
+               mrst_dev = pci_id_2_mrst_dev(pdev->device);
+               if (unlikely(!mrst_dev)) {
+                       printk(KERN_ERR FW_BUG "pmu: Unknown "
+                               "PCI device 0x%04X\n", pdev->device);
+                       continue;
+               }
+
+               if (mrst_dev->lss == 0)
+                       continue;        /* no LSS in our table */
+
+               pos = pci_find_capability(pdev, PCI_CAP_ID_VNDR);
+               if (!pos != 0) {
+                       printk(KERN_ERR FW_BUG "pmu: 0x%04X "
+                               "missing PCI Vendor Capability\n",
+                               pdev->device);
+                       continue;
+               }
+               pci_read_config_byte(pdev, pos + 4, &pci_config_lss);
+               if (!(pci_config_lss & PCI_VENDOR_CAP_LOG_SS_MASK)) {
+                       printk(KERN_ERR FW_BUG "pmu: 0x%04X "
+                               "invalid PCI Vendor Capability 0x%x "
+                               " expected LSS 0x%X\n",
+                               pdev->device, pci_config_lss, mrst_dev->lss);
+                       continue;
+               }
+               pci_config_lss &= PCI_VENDOR_CAP_LOG_ID_MASK;
+
+               if (mrst_dev->lss == pci_config_lss)
+                       continue;
+
+               printk(KERN_ERR FW_BUG "pmu: 0x%04X LSS = %d, expected %d\n",
+                       pdev->device, pci_config_lss, mrst_dev->lss);
+       }
+}
+
+/**
+ * pmu_probe
+ */
+static int __devinit pmu_probe(struct pci_dev *pdev,
+                                  const struct pci_device_id *pci_id)
+{
+       int ret;
+       struct mrst_pmu_reg *pmu;
+
+       /* Init the device */
+       ret = pci_enable_device(pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "Unable to Enable PCI device\n");
+               return ret;
+       }
+
+       ret = pci_request_regions(pdev, MRST_PMU_DRV_NAME);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting\n");
+               goto out_err1;
+       }
+
+       /* Map the memory of PMU reg base */
+       pmu = pci_iomap(pdev, 0, 0);
+       if (!pmu) {
+               dev_err(&pdev->dev, "Unable to map the PMU address space\n");
+               ret = -ENOMEM;
+               goto out_err2;
+       }
+
+#ifdef CONFIG_DEBUG_FS
+       /* /sys/kernel/debug/mrst_pmu */
+       (void) debugfs_create_file("mrst_pmu", S_IFREG | S_IRUGO,
+                               NULL, NULL, &devices_state_operations);
+#endif
+       pmu_reg = pmu;  /* success */
+
+       if (request_irq(pdev->irq, pmu_irq, 0, MRST_PMU_DRV_NAME, NULL)) {
+               dev_err(&pdev->dev, "Registering isr has failed\n");
+               ret = -1;
+               goto out_err3;
+       }
+
+       pmu_scu_firmware_debug();
+
+       pmu_write_wkc(S0I3_WAKE_SOURCES);       /* Enable S0i3 wakeup sources */
+
+       pmu_wait_ready();
+
+       pmu_write_ssc(D0I1_ACG_SSS_TARGET);     /* Enable Auto-Clock_Gating */
+       pmu_write_cmd(0x201);
+
+       spin_lock_init(&mrst_pmu_power_state_lock);
+
+       /* Enable the hardware interrupt */
+       pmu_irq_enable();
+       return 0;
+
+out_err3:
+       free_irq(pdev->irq, NULL);
+       pci_iounmap(pdev, pmu_reg);
+       pmu_reg = NULL;
+out_err2:
+       pci_release_region(pdev, 0);
+out_err1:
+       pci_disable_device(pdev);
+       return ret;
+}
+
+static void __devexit pmu_remove(struct pci_dev *pdev)
+{
+       dev_err(&pdev->dev, "Mid PM pmu_remove called\n");
+
+       /* Freeing up the irq */
+       free_irq(pdev->irq, NULL);
+
+       pci_iounmap(pdev, pmu_reg);
+       pmu_reg = NULL;
+
+       /* disable the current PCI device */
+       pci_release_region(pdev, 0);
+       pci_disable_device(pdev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(pmu_pci_ids) = {
+       { PCI_VDEVICE(INTEL, PCI_DEV_ID_MRST_PMU), 0 },
+       { }
+};
+
+MODULE_DEVICE_TABLE(pci, pmu_pci_ids);
+
+static struct pci_driver driver = {
+       .name = MRST_PMU_DRV_NAME,
+       .id_table = pmu_pci_ids,
+       .probe = pmu_probe,
+       .remove = __devexit_p(pmu_remove),
+};
+
+/**
+ * pmu_pci_register - register the PMU driver as PCI device
+ */
+static int __init pmu_pci_register(void)
+{
+       return pci_register_driver(&driver);
+}
+
+/* Register and probe via fs_initcall() to preceed device_initcall() */
+fs_initcall(pmu_pci_register);
+
+static void __exit mid_pci_cleanup(void)
+{
+       pci_unregister_driver(&driver);
+}
+
+static int ia_major;
+static int ia_minor;
+
+static int pmu_sfi_parse_oem(struct sfi_table_header *table)
+{
+       struct sfi_table_simple *sb;
+
+       sb = (struct sfi_table_simple *)table;
+       ia_major = (sb->pentry[1] >> 0) & 0xFFFF;
+       ia_minor = (sb->pentry[1] >> 16) & 0xFFFF;
+       printk(KERN_INFO "mrst_pmu: IA FW version v%x.%x\n",
+               ia_major, ia_minor);
+
+       return 0;
+}
+
+static int __init scu_fw_check(void)
+{
+       int ret;
+       u32 fw_version;
+
+       if (!pmu_reg)
+               return 0;       /* this driver didn't probe-out */
+
+       sfi_table_parse("OEMB", NULL, NULL, pmu_sfi_parse_oem);
+
+       if (ia_major < 0x6005 || ia_minor < 0x1525) {
+               WARN(1, "mrst_pmu: IA FW version too old\n");
+               return -1;
+       }
+
+       ret = intel_scu_ipc_command(IPCMSG_FW_REVISION, 0, NULL, 0,
+                                       &fw_version, 1);
+
+       if (ret) {
+               WARN(1, "mrst_pmu: IPC FW version? %d\n", ret);
+       } else {
+               int scu_major = (fw_version >> 8) & 0xFF;
+               int scu_minor = (fw_version >> 0) & 0xFF;
+
+               printk(KERN_INFO "mrst_pmu: firmware v%x\n", fw_version);
+
+               if ((scu_major >= 0xC0) && (scu_minor >= 0x49)) {
+                       printk(KERN_INFO "mrst_pmu: enabling S0i3\n");
+                       mrst_pmu_s0i3_enable = true;
+               } else {
+                       WARN(1, "mrst_pmu: S0i3 disabled, old firmware %X.%X",
+                                       scu_major, scu_minor);
+               }
+       }
+       return 0;
+}
+late_initcall(scu_fw_check);
+module_exit(mid_pci_cleanup);
diff --git a/arch/x86/platform/mrst/pmu.h b/arch/x86/platform/mrst/pmu.h
new file mode 100644 (file)
index 0000000..bfbfe64
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * mrst/pmu.h - private definitions for MRST Power Management Unit mrst/pmu.c
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _MRST_PMU_H_
+#define _MRST_PMU_H_
+
+#define PCI_DEV_ID_MRST_PMU            0x0810
+#define MRST_PMU_DRV_NAME              "mrst_pmu"
+#define        PCI_SUB_CLASS_MASK              0xFF00
+
+#define        PCI_VENDOR_CAP_LOG_ID_MASK      0x7F
+#define PCI_VENDOR_CAP_LOG_SS_MASK     0x80
+
+#define SUB_SYS_ALL_D0I1       0x01155555
+#define S0I3_WAKE_SOURCES      0x00001FFF
+
+#define PM_S0I3_COMMAND                                        \
+       ((0 << 31) |    /* Reserved */                  \
+       (0 << 30) |     /* Core must be idle */         \
+       (0xc2 << 22) |  /* ACK C6 trigger */            \
+       (3 << 19) |     /* Trigger on DMI message */    \
+       (3 << 16) |     /* Enter S0i3 */                \
+       (0 << 13) |     /* Numeric mode ID (sw) */      \
+       (3 << 9) |      /* Trigger mode */              \
+       (0 << 8) |      /* Do not interrupt */          \
+       (1 << 0))       /* Set configuration */
+
+#define        LSS_DMI         0
+#define        LSS_SD_HC0      1
+#define        LSS_SD_HC1      2
+#define        LSS_NAND        3
+#define        LSS_IMAGING     4
+#define        LSS_SECURITY    5
+#define        LSS_DISPLAY     6
+#define        LSS_USB_HC      7
+#define        LSS_USB_OTG     8
+#define        LSS_AUDIO       9
+#define        LSS_AUDIO_LPE   9
+#define        LSS_AUDIO_SSP   9
+#define        LSS_I2C0        10
+#define        LSS_I2C1        10
+#define        LSS_I2C2        10
+#define        LSS_KBD         10
+#define        LSS_SPI0        10
+#define        LSS_SPI1        10
+#define        LSS_SPI2        10
+#define        LSS_GPIO        10
+#define        LSS_SRAM        11      /* used by SCU, do not touch */
+#define        LSS_SD_HC2      12
+/* LSS hardware bits 15,14,13 are hardwired to 0, thus unusable */
+#define MRST_NUM_LSS   13
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+#define        SSMSK(mask, lss) ((mask) << ((lss) * 2))
+#define        D0      0
+#define        D0i1    1
+#define        D0i2    2
+#define        D0i3    3
+
+#define S0I3_SSS_TARGET        (               \
+       SSMSK(D0i1, LSS_DMI) |          \
+       SSMSK(D0i3, LSS_SD_HC0) |       \
+       SSMSK(D0i3, LSS_SD_HC1) |       \
+       SSMSK(D0i3, LSS_NAND) |         \
+       SSMSK(D0i3, LSS_SD_HC2) |       \
+       SSMSK(D0i3, LSS_IMAGING) |      \
+       SSMSK(D0i3, LSS_SECURITY) |     \
+       SSMSK(D0i3, LSS_DISPLAY) |      \
+       SSMSK(D0i3, LSS_USB_HC) |       \
+       SSMSK(D0i3, LSS_USB_OTG) |      \
+       SSMSK(D0i3, LSS_AUDIO) |        \
+       SSMSK(D0i1, LSS_I2C0))
+
+/*
+ * D0i1 on Langwell is Autonomous Clock Gating (ACG).
+ * Enable ACG on every LSS except camera and audio
+ */
+#define D0I1_ACG_SSS_TARGET     \
+       (SUB_SYS_ALL_D0I1 & ~SSMSK(D0i1, LSS_IMAGING) & ~SSMSK(D0i1, LSS_AUDIO))
+
+enum cm_mode {
+       CM_NOP,                 /* ignore the config mode value */
+       CM_IMMEDIATE,
+       CM_DELAY,
+       CM_TRIGGER,
+       CM_INVALID
+};
+
+enum sys_state {
+       SYS_STATE_S0I0,
+       SYS_STATE_S0I1,
+       SYS_STATE_S0I2,
+       SYS_STATE_S0I3,
+       SYS_STATE_S3,
+       SYS_STATE_S5
+};
+
+#define SET_CFG_CMD    1
+
+enum int_status {
+       INT_SPURIOUS = 0,
+       INT_CMD_DONE = 1,
+       INT_CMD_ERR = 2,
+       INT_WAKE_RX = 3,
+       INT_SS_ERROR = 4,
+       INT_S0IX_MISS = 5,
+       INT_NO_ACKC6 = 6,
+       INT_INVALID = 7,
+};
+
+/* PMU register interface */
+static struct mrst_pmu_reg {
+       u32 pm_sts;             /* 0x00 */
+       u32 pm_cmd;             /* 0x04 */
+       u32 pm_ics;             /* 0x08 */
+       u32 _resv1;             /* 0x0C */
+       u32 pm_wkc[2];          /* 0x10 */
+       u32 pm_wks[2];          /* 0x18 */
+       u32 pm_ssc[4];          /* 0x20 */
+       u32 pm_sss[4];          /* 0x30 */
+       u32 pm_wssc[4];         /* 0x40 */
+       u32 pm_c3c4;            /* 0x50 */
+       u32 pm_c5c6;            /* 0x54 */
+       u32 pm_msi_disable;     /* 0x58 */
+} *pmu_reg;
+
+static inline u32 pmu_read_sts(void) { return readl(&pmu_reg->pm_sts); }
+static inline u32 pmu_read_ics(void) { return readl(&pmu_reg->pm_ics); }
+static inline u32 pmu_read_wks(void) { return readl(&pmu_reg->pm_wks[0]); }
+static inline u32 pmu_read_sss(void) { return readl(&pmu_reg->pm_sss[0]); }
+
+static inline void pmu_write_cmd(u32 arg) { writel(arg, &pmu_reg->pm_cmd); }
+static inline void pmu_write_ics(u32 arg) { writel(arg, &pmu_reg->pm_ics); }
+static inline void pmu_write_wkc(u32 arg) { writel(arg, &pmu_reg->pm_wkc[0]); }
+static inline void pmu_write_ssc(u32 arg) { writel(arg, &pmu_reg->pm_ssc[0]); }
+static inline void pmu_write_wssc(u32 arg)
+                                       { writel(arg, &pmu_reg->pm_wssc[0]); }
+
+static inline void pmu_msi_enable(void) { writel(0, &pmu_reg->pm_msi_disable); }
+static inline u32 pmu_msi_is_disabled(void)
+                               { return readl(&pmu_reg->pm_msi_disable); }
+
+union pmu_pm_ics {
+       struct {
+               u32 cause:8;
+               u32 enable:1;
+               u32 pending:1;
+               u32 reserved:22;
+       } bits;
+       u32 value;
+};
+
+static inline void pmu_irq_enable(void)
+{
+       union pmu_pm_ics pmu_ics;
+
+       pmu_ics.value = pmu_read_ics();
+       pmu_ics.bits.enable = 1;
+       pmu_write_ics(pmu_ics.value);
+}
+
+union pmu_pm_status {
+       struct {
+               u32 pmu_rev:8;
+               u32 pmu_busy:1;
+               u32 mode_id:4;
+               u32 Reserved:19;
+       } pmu_status_parts;
+       u32 pmu_status_value;
+};
+
+static inline int pmu_read_busy_status(void)
+{
+       union pmu_pm_status result;
+
+       result.pmu_status_value = pmu_read_sts();
+
+       return result.pmu_status_parts.pmu_busy;
+}
+
+/* pmu set config parameters */
+struct cfg_delay_param_t {
+       u32 cmd:8;
+       u32 ioc:1;
+       u32 cfg_mode:4;
+       u32 mode_id:3;
+       u32 sys_state:3;
+       u32 cfg_delay:8;
+       u32 rsvd:5;
+};
+
+struct cfg_trig_param_t {
+       u32 cmd:8;
+       u32 ioc:1;
+       u32 cfg_mode:4;
+       u32 mode_id:3;
+       u32 sys_state:3;
+       u32 cfg_trig_type:3;
+       u32 cfg_trig_val:8;
+       u32 cmbi:1;
+       u32 rsvd1:1;
+};
+
+union pmu_pm_set_cfg_cmd_t {
+       union {
+               struct cfg_delay_param_t d_param;
+               struct cfg_trig_param_t t_param;
+       } pmu2_params;
+       u32 pmu_pm_set_cfg_cmd_value;
+};
+
+#ifdef FUTURE_PATCH
+extern int mrst_s0i3_entry(u32 regval, u32 *regaddr);
+#else
+static inline int mrst_s0i3_entry(u32 regval, u32 *regaddr) { return -1; }
+#endif
+#endif
index 45e94aca5bcef5aed5546c4a0cd25e1de5521e4e..3326204e251f3108650944ef1d558c98745c1935 100644 (file)
@@ -15,7 +15,7 @@ obj-y         := enlighten.o setup.o multicalls.o mmu.o irq.o \
                        grant-table.o suspend.o platform-pci-unplug.o \
                        p2m.o
 
-obj-$(CONFIG_FUNCTION_TRACER) += trace.o
+obj-$(CONFIG_FTRACE) += trace.o
 
 obj-$(CONFIG_SMP)              += smp.o
 obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= spinlock.o
index 60aeeb56948f753f87559f3e4ff8c1f8704d21a4..df118a825f395cbb4e79b9282594caa77aa6a4ca 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/mm.h>
 #include <linux/pm.h>
 #include <linux/memblock.h>
+#include <linux/cpuidle.h>
 
 #include <asm/elf.h>
 #include <asm/vdso.h>
@@ -92,8 +93,6 @@ static unsigned long __init xen_release_chunk(phys_addr_t start_addr,
        if (end <= start)
                return 0;
 
-       printk(KERN_INFO "xen_release_chunk: looking at area pfn %lx-%lx: ",
-              start, end);
        for(pfn = start; pfn < end; pfn++) {
                unsigned long mfn = pfn_to_mfn(pfn);
 
@@ -106,14 +105,14 @@ static unsigned long __init xen_release_chunk(phys_addr_t start_addr,
 
                ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation,
                                           &reservation);
-               WARN(ret != 1, "Failed to release memory %lx-%lx err=%d\n",
-                    start, end, ret);
+               WARN(ret != 1, "Failed to release pfn %lx err=%d\n", pfn, ret);
                if (ret == 1) {
                        __set_phys_to_machine(pfn, INVALID_P2M_ENTRY);
                        len++;
                }
        }
-       printk(KERN_CONT "%ld pages freed\n", len);
+       printk(KERN_INFO "Freeing  %lx-%lx pfn range: %lu pages freed\n",
+              start, end, len);
 
        return len;
 }
@@ -139,7 +138,7 @@ static unsigned long __init xen_return_unused_memory(unsigned long max_pfn,
        if (last_end < max_addr)
                released += xen_release_chunk(last_end, max_addr);
 
-       printk(KERN_INFO "released %ld pages of unused memory\n", released);
+       printk(KERN_INFO "released %lu pages of unused memory\n", released);
        return released;
 }
 
@@ -426,7 +425,7 @@ void __init xen_arch_setup(void)
 #ifdef CONFIG_X86_32
        boot_cpu_data.hlt_works_ok = 1;
 #endif
-       pm_idle = default_idle;
+       disable_cpuidle();
        boot_option_idle_override = IDLE_HALT;
 
        fiddle_vdso();
index 734beba2a08c41fbfd48b5d5f91865c79614d376..520022d1a18134715b6ebc16a86341aa3a7f104a 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/ftrace.h>
+#include <xen/interface/xen.h>
 
 #define N(x)   [__HYPERVISOR_##x] = "("#x")"
 static const char *xen_hypercall_names[] = {
index b850bedad229ec14944b60665c83c195215a7776..b627558c461fa7a1d9eaf6ea447db093852c8274 100644 (file)
@@ -1368,8 +1368,10 @@ static bool should_fail_request(struct hd_struct *part, unsigned int bytes)
 
 static int __init fail_make_request_debugfs(void)
 {
-       return init_fault_attr_dentries(&fail_make_request,
-                                       "fail_make_request");
+       struct dentry *dir = fault_create_debugfs_attr("fail_make_request",
+                                               NULL, &fail_make_request);
+
+       return IS_ERR(dir) ? PTR_ERR(dir) : 0;
 }
 
 late_initcall(fail_make_request_debugfs);
index 4f0c06c7a3388062f5aa58a8ffbe56de5d7c1f1e..780354888958cd7ea03aef2c1654b325bc9fb24e 100644 (file)
@@ -28,7 +28,10 @@ int blk_should_fake_timeout(struct request_queue *q)
 
 static int __init fail_io_timeout_debugfs(void)
 {
-       return init_fault_attr_dentries(&fail_io_timeout, "fail_io_timeout");
+       struct dentry *dir = fault_create_debugfs_attr("fail_io_timeout",
+                                               NULL, &fail_io_timeout);
+
+       return IS_ERR(dir) ? PTR_ERR(dir) : 0;
 }
 
 late_initcall(fail_io_timeout_debugfs);
index 73863d86f022c659506997f0496bac3f3673865b..76dc02f155748af02278d465e3948e7c1a214cf0 100644 (file)
@@ -126,6 +126,12 @@ u8 ACPI_INIT_GLOBAL(acpi_gbl_copy_dsdt_locally, FALSE);
  */
 u8 ACPI_INIT_GLOBAL(acpi_gbl_truncate_io_addresses, FALSE);
 
+/*
+ * Disable runtime checking and repair of values returned by control methods.
+ * Use only if the repair is causing a problem on a particular machine.
+ */
+u8 ACPI_INIT_GLOBAL(acpi_gbl_disable_auto_repair, FALSE);
+
 /* acpi_gbl_FADT is a local copy of the FADT, converted to a common format. */
 
 struct acpi_table_fadt acpi_gbl_FADT;
index c7f743ca395bbcaf91d4b046cee29b804b871c3a..5552125d8340ef11782f4aec94595e7444cb1774 100644 (file)
@@ -357,6 +357,7 @@ struct acpi_predefined_data {
        char *pathname;
        const union acpi_predefined_info *predefined;
        union acpi_operand_object *parent_package;
+       struct acpi_namespace_node *node;
        u32 flags;
        u8 node_flags;
 };
index 94e73c97cf85a83eaf6be8ceae3c347abb68f40e..c445cca490ea4b1489983ba4866b0fb746c3de21 100644 (file)
@@ -468,6 +468,7 @@ static const union acpi_predefined_info predefined_names[] =
        {{"_SWS", 0, ACPI_RTYPE_INTEGER}},
        {{"_TC1", 0, ACPI_RTYPE_INTEGER}},
        {{"_TC2", 0, ACPI_RTYPE_INTEGER}},
+       {{"_TDL", 0, ACPI_RTYPE_INTEGER}},
        {{"_TIP", 1, ACPI_RTYPE_INTEGER}},
        {{"_TIV", 1, ACPI_RTYPE_INTEGER}},
        {{"_TMP", 0, ACPI_RTYPE_INTEGER}},
index 9fb03fa8ffde39b253a88cfe52359972491c70b4..c845c8089f39c4190f4896889d308ef546195549 100644 (file)
@@ -193,14 +193,20 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node,
        }
 
        /*
-        * 1) We have a return value, but if one wasn't expected, just exit, this is
-        * not a problem. For example, if the "Implicit Return" feature is
-        * enabled, methods will always return a value.
+        * Return value validation and possible repair.
         *
-        * 2) If the return value can be of any type, then we cannot perform any
-        * validation, exit.
+        * 1) Don't perform return value validation/repair if this feature
+        * has been disabled via a global option.
+        *
+        * 2) We have a return value, but if one wasn't expected, just exit,
+        * this is not a problem. For example, if the "Implicit Return"
+        * feature is enabled, methods will always return a value.
+        *
+        * 3) If the return value can be of any type, then we cannot perform
+        * any validation, just exit.
         */
-       if ((!predefined->info.expected_btypes) ||
+       if (acpi_gbl_disable_auto_repair ||
+           (!predefined->info.expected_btypes) ||
            (predefined->info.expected_btypes == ACPI_RTYPE_ALL)) {
                goto cleanup;
        }
@@ -212,6 +218,7 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node,
                goto cleanup;
        }
        data->predefined = predefined;
+       data->node = node;
        data->node_flags = node->flags;
        data->pathname = pathname;
 
index 973883babee1fa3a98718c43b3903de75a9ec83e..024c4f263f872fc550567e56a5d4a32c369071de 100644 (file)
@@ -503,6 +503,21 @@ acpi_ns_repair_TSS(struct acpi_predefined_data *data,
 {
        union acpi_operand_object *return_object = *return_object_ptr;
        acpi_status status;
+       struct acpi_namespace_node *node;
+
+       /*
+        * We can only sort the _TSS return package if there is no _PSS in the
+        * same scope. This is because if _PSS is present, the ACPI specification
+        * dictates that the _TSS Power Dissipation field is to be ignored, and
+        * therefore some BIOSs leave garbage values in the _TSS Power field(s).
+        * In this case, it is best to just return the _TSS package as-is.
+        * (May, 2011)
+        */
+       status =
+           acpi_ns_get_node(data->node, "^_PSS", ACPI_NS_NO_UPSEARCH, &node);
+       if (ACPI_SUCCESS(status)) {
+               return (AE_OK);
+       }
 
        status = acpi_ns_check_sorted_list(data, return_object, 5, 1,
                                           ACPI_SORT_DESCENDING,
index 48db0944ce4a00e1256b4f888b4497e2ee584d2e..62365f6075dda417e7204d55105707ef1a64eaf9 100644 (file)
@@ -126,12 +126,29 @@ acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index)
        }
 
        /*
-        * Originally, we checked the table signature for "SSDT" or "PSDT" here.
-        * Next, we added support for OEMx tables, signature "OEM".
-        * Valid tables were encountered with a null signature, so we've just
-        * given up on validating the signature, since it seems to be a waste
-        * of code. The original code was removed (05/2008).
+        * Validate the incoming table signature.
+        *
+        * 1) Originally, we checked the table signature for "SSDT" or "PSDT".
+        * 2) We added support for OEMx tables, signature "OEM".
+        * 3) Valid tables were encountered with a null signature, so we just
+        *    gave up on validating the signature, (05/2008).
+        * 4) We encountered non-AML tables such as the MADT, which caused
+        *    interpreter errors and kernel faults. So now, we once again allow
+        *    only "SSDT", "OEMx", and now, also a null signature. (05/2011).
         */
+       if ((table_desc->pointer->signature[0] != 0x00) &&
+           (!ACPI_COMPARE_NAME(table_desc->pointer->signature, ACPI_SIG_SSDT))
+           && (ACPI_STRNCMP(table_desc->pointer->signature, "OEM", 3))) {
+               ACPI_ERROR((AE_INFO,
+                           "Table has invalid signature [%4.4s] (0x%8.8X), must be SSDT or OEMx",
+                           acpi_ut_valid_acpi_name(*(u32 *)table_desc->
+                                                   pointer->
+                                                   signature) ? table_desc->
+                           pointer->signature : "????",
+                           *(u32 *)table_desc->pointer->signature));
+
+               return_ACPI_STATUS(AE_BAD_SIGNATURE);
+       }
 
        (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
 
index f739a70b1c708a0084afa2e8e6605f401c7bfd94..c34aa51af4eed85a99bc9aad7720e4dd582f47a8 100644 (file)
@@ -10,9 +10,11 @@ config ACPI_APEI
          error injection.
 
 config ACPI_APEI_GHES
-       tristate "APEI Generic Hardware Error Source"
+       bool "APEI Generic Hardware Error Source"
        depends on ACPI_APEI && X86
        select ACPI_HED
+       select LLIST
+       select GENERIC_ALLOCATOR
        help
          Generic Hardware Error Source provides a way to report
          platform hardware errors (such as that from chipset). It
@@ -30,6 +32,13 @@ config ACPI_APEI_PCIEAER
          PCIe AER errors may be reported via APEI firmware first mode.
          Turn on this option to enable the corresponding support.
 
+config ACPI_APEI_MEMORY_FAILURE
+       bool "APEI memory error recovering support"
+       depends on ACPI_APEI && MEMORY_FAILURE
+       help
+         Memory errors may be reported via APEI firmware first mode.
+         Turn on this option to enable the memory recovering support.
+
 config ACPI_APEI_EINJ
        tristate "APEI Error INJection (EINJ)"
        depends on ACPI_APEI && DEBUG_FS
index 4a904a4bf05f83a1b952ec2365811f8c5db931e1..8041248fce9ba245a6eeeedf4adb4dd7a2b5ee20 100644 (file)
@@ -157,9 +157,10 @@ EXPORT_SYMBOL_GPL(apei_exec_noop);
  * Interpret the specified action. Go through whole action table,
  * execute all instructions belong to the action.
  */
-int apei_exec_run(struct apei_exec_context *ctx, u8 action)
+int __apei_exec_run(struct apei_exec_context *ctx, u8 action,
+                   bool optional)
 {
-       int rc;
+       int rc = -ENOENT;
        u32 i, ip;
        struct acpi_whea_header *entry;
        apei_exec_ins_func_t run;
@@ -198,9 +199,9 @@ rewind:
                        goto rewind;
        }
 
-       return 0;
+       return !optional && rc < 0 ? rc : 0;
 }
-EXPORT_SYMBOL_GPL(apei_exec_run);
+EXPORT_SYMBOL_GPL(__apei_exec_run);
 
 typedef int (*apei_exec_entry_func_t)(struct apei_exec_context *ctx,
                                      struct acpi_whea_header *entry,
@@ -603,3 +604,29 @@ struct dentry *apei_get_debugfs_dir(void)
        return dapei;
 }
 EXPORT_SYMBOL_GPL(apei_get_debugfs_dir);
+
+int apei_osc_setup(void)
+{
+       static u8 whea_uuid_str[] = "ed855e0c-6c90-47bf-a62a-26de0fc5ad5c";
+       acpi_handle handle;
+       u32 capbuf[3];
+       struct acpi_osc_context context = {
+               .uuid_str       = whea_uuid_str,
+               .rev            = 1,
+               .cap.length     = sizeof(capbuf),
+               .cap.pointer    = capbuf,
+       };
+
+       capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
+       capbuf[OSC_SUPPORT_TYPE] = 0;
+       capbuf[OSC_CONTROL_TYPE] = 0;
+
+       if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))
+           || ACPI_FAILURE(acpi_run_osc(handle, &context)))
+               return -EIO;
+       else {
+               kfree(context.ret.pointer);
+               return 0;
+       }
+}
+EXPORT_SYMBOL_GPL(apei_osc_setup);
index ef0581f2094de6c77128f65f64cf2f63dd728d1e..f57050e7a5e756ceb1f8885ce01030d0ac593ea4 100644 (file)
@@ -50,7 +50,18 @@ static inline u64 apei_exec_ctx_get_output(struct apei_exec_context *ctx)
        return ctx->value;
 }
 
-int apei_exec_run(struct apei_exec_context *ctx, u8 action);
+int __apei_exec_run(struct apei_exec_context *ctx, u8 action, bool optional);
+
+static inline int apei_exec_run(struct apei_exec_context *ctx, u8 action)
+{
+       return __apei_exec_run(ctx, action, 0);
+}
+
+/* It is optional whether the firmware provides the action */
+static inline int apei_exec_run_optional(struct apei_exec_context *ctx, u8 action)
+{
+       return __apei_exec_run(ctx, action, 1);
+}
 
 /* Common instruction implementation */
 
@@ -113,4 +124,6 @@ void apei_estatus_print(const char *pfx,
                        const struct acpi_hest_generic_status *estatus);
 int apei_estatus_check_header(const struct acpi_hest_generic_status *estatus);
 int apei_estatus_check(const struct acpi_hest_generic_status *estatus);
+
+int apei_osc_setup(void);
 #endif
index f74b2ea11f21128dc9c0beff600f18a57d7fbea0..589b96c38704db0bf803842aaf3c6b22b890cd06 100644 (file)
@@ -46,7 +46,8 @@
  * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the
  * EINJ table through an unpublished extension. Use with caution as
  * most will ignore the parameter and make their own choice of address
- * for error injection.
+ * for error injection.  This extension is used only if
+ * param_extension module parameter is specified.
  */
 struct einj_parameter {
        u64 type;
@@ -65,6 +66,9 @@ struct einj_parameter {
        ((struct acpi_whea_header *)((char *)(tab) +                    \
                                    sizeof(struct acpi_table_einj)))
 
+static bool param_extension;
+module_param(param_extension, bool, 0);
+
 static struct acpi_table_einj *einj_tab;
 
 static struct apei_resources einj_resources;
@@ -285,7 +289,7 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
 
        einj_exec_ctx_init(&ctx);
 
-       rc = apei_exec_run(&ctx, ACPI_EINJ_BEGIN_OPERATION);
+       rc = apei_exec_run_optional(&ctx, ACPI_EINJ_BEGIN_OPERATION);
        if (rc)
                return rc;
        apei_exec_ctx_set_input(&ctx, type);
@@ -323,7 +327,7 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
        rc = __einj_error_trigger(trigger_paddr);
        if (rc)
                return rc;
-       rc = apei_exec_run(&ctx, ACPI_EINJ_END_OPERATION);
+       rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION);
 
        return rc;
 }
@@ -489,14 +493,6 @@ static int __init einj_init(void)
                                     einj_debug_dir, NULL, &error_type_fops);
        if (!fentry)
                goto err_cleanup;
-       fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR,
-                                   einj_debug_dir, &error_param1);
-       if (!fentry)
-               goto err_cleanup;
-       fentry = debugfs_create_x64("param2", S_IRUSR | S_IWUSR,
-                                   einj_debug_dir, &error_param2);
-       if (!fentry)
-               goto err_cleanup;
        fentry = debugfs_create_file("error_inject", S_IWUSR,
                                     einj_debug_dir, NULL, &error_inject_fops);
        if (!fentry)
@@ -513,12 +509,23 @@ static int __init einj_init(void)
        rc = apei_exec_pre_map_gars(&ctx);
        if (rc)
                goto err_release;
-       param_paddr = einj_get_parameter_address();
-       if (param_paddr) {
-               einj_param = ioremap(param_paddr, sizeof(*einj_param));
-               rc = -ENOMEM;
-               if (!einj_param)
-                       goto err_unmap;
+       if (param_extension) {
+               param_paddr = einj_get_parameter_address();
+               if (param_paddr) {
+                       einj_param = ioremap(param_paddr, sizeof(*einj_param));
+                       rc = -ENOMEM;
+                       if (!einj_param)
+                               goto err_unmap;
+                       fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR,
+                                                   einj_debug_dir, &error_param1);
+                       if (!fentry)
+                               goto err_unmap;
+                       fentry = debugfs_create_x64("param2", S_IRUSR | S_IWUSR,
+                                                   einj_debug_dir, &error_param2);
+                       if (!fentry)
+                               goto err_unmap;
+               } else
+                       pr_warn(EINJ_PFX "Parameter extension is not supported.\n");
        }
 
        pr_info(EINJ_PFX "Error INJection is initialized.\n");
@@ -526,6 +533,8 @@ static int __init einj_init(void)
        return 0;
 
 err_unmap:
+       if (einj_param)
+               iounmap(einj_param);
        apei_exec_post_unmap_gars(&ctx);
 err_release:
        apei_resources_release(&einj_resources);
index a4cfb64c86a157bcf8ae22730d6a7943e31af7e0..903549df809bf1b02cc95df234dd7442cd3fb3ee 100644 (file)
@@ -33,7 +33,7 @@
 
 #define ERST_DBG_PFX                   "ERST DBG: "
 
-#define ERST_DBG_RECORD_LEN_MAX                4096
+#define ERST_DBG_RECORD_LEN_MAX                0x4000
 
 static void *erst_dbg_buf;
 static unsigned int erst_dbg_buf_len;
@@ -213,6 +213,10 @@ static struct miscdevice erst_dbg_dev = {
 
 static __init int erst_dbg_init(void)
 {
+       if (erst_disable) {
+               pr_info(ERST_DBG_PFX "ERST support is disabled.\n");
+               return -ENODEV;
+       }
        return misc_register(&erst_dbg_dev);
 }
 
index e6cef8e1b53455db9927668c1da1157c364e6b6e..2ca59dc69f7f6b034660bfdc04462230880456f9 100644 (file)
@@ -642,7 +642,7 @@ static int __erst_write_to_storage(u64 offset)
        int rc;
 
        erst_exec_ctx_init(&ctx);
-       rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_WRITE);
+       rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_WRITE);
        if (rc)
                return rc;
        apei_exec_ctx_set_input(&ctx, offset);
@@ -666,7 +666,7 @@ static int __erst_write_to_storage(u64 offset)
        if (rc)
                return rc;
        val = apei_exec_ctx_get_output(&ctx);
-       rc = apei_exec_run(&ctx, ACPI_ERST_END);
+       rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
        if (rc)
                return rc;
 
@@ -681,7 +681,7 @@ static int __erst_read_from_storage(u64 record_id, u64 offset)
        int rc;
 
        erst_exec_ctx_init(&ctx);
-       rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_READ);
+       rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_READ);
        if (rc)
                return rc;
        apei_exec_ctx_set_input(&ctx, offset);
@@ -709,7 +709,7 @@ static int __erst_read_from_storage(u64 record_id, u64 offset)
        if (rc)
                return rc;
        val = apei_exec_ctx_get_output(&ctx);
-       rc = apei_exec_run(&ctx, ACPI_ERST_END);
+       rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
        if (rc)
                return rc;
 
@@ -724,7 +724,7 @@ static int __erst_clear_from_storage(u64 record_id)
        int rc;
 
        erst_exec_ctx_init(&ctx);
-       rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_CLEAR);
+       rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_CLEAR);
        if (rc)
                return rc;
        apei_exec_ctx_set_input(&ctx, record_id);
@@ -748,7 +748,7 @@ static int __erst_clear_from_storage(u64 record_id)
        if (rc)
                return rc;
        val = apei_exec_ctx_get_output(&ctx);
-       rc = apei_exec_run(&ctx, ACPI_ERST_END);
+       rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);
        if (rc)
                return rc;
 
@@ -932,8 +932,11 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)
 static int erst_open_pstore(struct pstore_info *psi);
 static int erst_close_pstore(struct pstore_info *psi);
 static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
-                      struct timespec *time);
-static u64 erst_writer(enum pstore_type_id type, size_t size);
+                          struct timespec *time, struct pstore_info *psi);
+static u64 erst_writer(enum pstore_type_id type, unsigned int part,
+                      size_t size, struct pstore_info *psi);
+static int erst_clearer(enum pstore_type_id type, u64 id,
+                       struct pstore_info *psi);
 
 static struct pstore_info erst_info = {
        .owner          = THIS_MODULE,
@@ -942,7 +945,7 @@ static struct pstore_info erst_info = {
        .close          = erst_close_pstore,
        .read           = erst_reader,
        .write          = erst_writer,
-       .erase          = erst_clear
+       .erase          = erst_clearer
 };
 
 #define CPER_CREATOR_PSTORE                                            \
@@ -983,7 +986,7 @@ static int erst_close_pstore(struct pstore_info *psi)
 }
 
 static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
-                      struct timespec *time)
+                          struct timespec *time, struct pstore_info *psi)
 {
        int rc;
        ssize_t len = 0;
@@ -1037,7 +1040,8 @@ out:
        return (rc < 0) ? rc : (len - sizeof(*rcd));
 }
 
-static u64 erst_writer(enum pstore_type_id type, size_t size)
+static u64 erst_writer(enum pstore_type_id type, unsigned int part,
+                      size_t size, struct pstore_info *psi)
 {
        struct cper_pstore_record *rcd = (struct cper_pstore_record *)
                                        (erst_info.buf - sizeof(*rcd));
@@ -1080,6 +1084,12 @@ static u64 erst_writer(enum pstore_type_id type, size_t size)
        return rcd->hdr.record_id;
 }
 
+static int erst_clearer(enum pstore_type_id type, u64 id,
+                       struct pstore_info *psi)
+{
+       return erst_clear(id);
+}
+
 static int __init erst_init(void)
 {
        int rc = 0;
index f703b2881153f57c9eb01b1a2da47ae45e8a4af3..0784f99a4665ded14affdfd502db253653b30a2e 100644 (file)
@@ -12,7 +12,7 @@
  * For more information about Generic Hardware Error Source, please
  * refer to ACPI Specification version 4.0, section 17.3.2.6
  *
- * Copyright 2010 Intel Corp.
+ * Copyright 2010,2011 Intel Corp.
  *   Author: Huang Ying <ying.huang@intel.com>
  *
  * This program is free software; you can redistribute it and/or
@@ -42,6 +42,9 @@
 #include <linux/mutex.h>
 #include <linux/ratelimit.h>
 #include <linux/vmalloc.h>
+#include <linux/irq_work.h>
+#include <linux/llist.h>
+#include <linux/genalloc.h>
 #include <acpi/apei.h>
 #include <acpi/atomicio.h>
 #include <acpi/hed.h>
 #define GHES_PFX       "GHES: "
 
 #define GHES_ESTATUS_MAX_SIZE          65536
+#define GHES_ESOURCE_PREALLOC_MAX_SIZE 65536
+
+#define GHES_ESTATUS_POOL_MIN_ALLOC_ORDER 3
+
+/* This is just an estimation for memory pool allocation */
+#define GHES_ESTATUS_CACHE_AVG_SIZE    512
+
+#define GHES_ESTATUS_CACHES_SIZE       4
+
+#define GHES_ESTATUS_IN_CACHE_MAX_NSEC 10000000000ULL
+/* Prevent too many caches are allocated because of RCU */
+#define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2)
+
+#define GHES_ESTATUS_CACHE_LEN(estatus_len)                    \
+       (sizeof(struct ghes_estatus_cache) + (estatus_len))
+#define GHES_ESTATUS_FROM_CACHE(estatus_cache)                 \
+       ((struct acpi_hest_generic_status *)                    \
+        ((struct ghes_estatus_cache *)(estatus_cache) + 1))
+
+#define GHES_ESTATUS_NODE_LEN(estatus_len)                     \
+       (sizeof(struct ghes_estatus_node) + (estatus_len))
+#define GHES_ESTATUS_FROM_NODE(estatus_node)                           \
+       ((struct acpi_hest_generic_status *)                            \
+        ((struct ghes_estatus_node *)(estatus_node) + 1))
 
 /*
  * One struct ghes is created for each generic hardware error source.
@@ -77,6 +104,22 @@ struct ghes {
        };
 };
 
+struct ghes_estatus_node {
+       struct llist_node llnode;
+       struct acpi_hest_generic *generic;
+};
+
+struct ghes_estatus_cache {
+       u32 estatus_len;
+       atomic_t count;
+       struct acpi_hest_generic *generic;
+       unsigned long long time_in;
+       struct rcu_head rcu;
+};
+
+int ghes_disable;
+module_param_named(disable, ghes_disable, bool, 0);
+
 static int ghes_panic_timeout  __read_mostly = 30;
 
 /*
@@ -121,6 +164,22 @@ static struct vm_struct *ghes_ioremap_area;
 static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi);
 static DEFINE_SPINLOCK(ghes_ioremap_lock_irq);
 
+/*
+ * printk is not safe in NMI context.  So in NMI handler, we allocate
+ * required memory from lock-less memory allocator
+ * (ghes_estatus_pool), save estatus into it, put them into lock-less
+ * list (ghes_estatus_llist), then delay printk into IRQ context via
+ * irq_work (ghes_proc_irq_work).  ghes_estatus_size_request record
+ * required pool size by all NMI error source.
+ */
+static struct gen_pool *ghes_estatus_pool;
+static unsigned long ghes_estatus_pool_size_request;
+static struct llist_head ghes_estatus_llist;
+static struct irq_work ghes_proc_irq_work;
+
+struct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
+static atomic_t ghes_estatus_cache_alloced;
+
 static int ghes_ioremap_init(void)
 {
        ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES,
@@ -180,6 +239,55 @@ static void ghes_iounmap_irq(void __iomem *vaddr_ptr)
        __flush_tlb_one(vaddr);
 }
 
+static int ghes_estatus_pool_init(void)
+{
+       ghes_estatus_pool = gen_pool_create(GHES_ESTATUS_POOL_MIN_ALLOC_ORDER, -1);
+       if (!ghes_estatus_pool)
+               return -ENOMEM;
+       return 0;
+}
+
+static void ghes_estatus_pool_free_chunk_page(struct gen_pool *pool,
+                                             struct gen_pool_chunk *chunk,
+                                             void *data)
+{
+       free_page(chunk->start_addr);
+}
+
+static void ghes_estatus_pool_exit(void)
+{
+       gen_pool_for_each_chunk(ghes_estatus_pool,
+                               ghes_estatus_pool_free_chunk_page, NULL);
+       gen_pool_destroy(ghes_estatus_pool);
+}
+
+static int ghes_estatus_pool_expand(unsigned long len)
+{
+       unsigned long i, pages, size, addr;
+       int ret;
+
+       ghes_estatus_pool_size_request += PAGE_ALIGN(len);
+       size = gen_pool_size(ghes_estatus_pool);
+       if (size >= ghes_estatus_pool_size_request)
+               return 0;
+       pages = (ghes_estatus_pool_size_request - size) / PAGE_SIZE;
+       for (i = 0; i < pages; i++) {
+               addr = __get_free_page(GFP_KERNEL);
+               if (!addr)
+                       return -ENOMEM;
+               ret = gen_pool_add(ghes_estatus_pool, addr, PAGE_SIZE, -1);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void ghes_estatus_pool_shrink(unsigned long len)
+{
+       ghes_estatus_pool_size_request -= PAGE_ALIGN(len);
+}
+
 static struct ghes *ghes_new(struct acpi_hest_generic *generic)
 {
        struct ghes *ghes;
@@ -341,43 +449,196 @@ static void ghes_clear_estatus(struct ghes *ghes)
        ghes->flags &= ~GHES_TO_CLEAR;
 }
 
-static void ghes_do_proc(struct ghes *ghes)
+static void ghes_do_proc(const struct acpi_hest_generic_status *estatus)
 {
-       int sev, processed = 0;
+       int sev, sec_sev;
        struct acpi_hest_generic_data *gdata;
 
-       sev = ghes_severity(ghes->estatus->error_severity);
-       apei_estatus_for_each_section(ghes->estatus, gdata) {
-#ifdef CONFIG_X86_MCE
+       sev = ghes_severity(estatus->error_severity);
+       apei_estatus_for_each_section(estatus, gdata) {
+               sec_sev = ghes_severity(gdata->error_severity);
                if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
                                 CPER_SEC_PLATFORM_MEM)) {
-                       apei_mce_report_mem_error(
-                               sev == GHES_SEV_CORRECTED,
-                               (struct cper_sec_mem_err *)(gdata+1));
-                       processed = 1;
-               }
+                       struct cper_sec_mem_err *mem_err;
+                       mem_err = (struct cper_sec_mem_err *)(gdata+1);
+#ifdef CONFIG_X86_MCE
+                       apei_mce_report_mem_error(sev == GHES_SEV_CORRECTED,
+                                                 mem_err);
 #endif
+#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE
+                       if (sev == GHES_SEV_RECOVERABLE &&
+                           sec_sev == GHES_SEV_RECOVERABLE &&
+                           mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) {
+                               unsigned long pfn;
+                               pfn = mem_err->physical_addr >> PAGE_SHIFT;
+                               memory_failure_queue(pfn, 0, 0);
+                       }
+#endif
+               }
        }
 }
 
-static void ghes_print_estatus(const char *pfx, struct ghes *ghes)
+static void __ghes_print_estatus(const char *pfx,
+                                const struct acpi_hest_generic *generic,
+                                const struct acpi_hest_generic_status *estatus)
 {
-       /* Not more than 2 messages every 5 seconds */
-       static DEFINE_RATELIMIT_STATE(ratelimit, 5*HZ, 2);
-
        if (pfx == NULL) {
-               if (ghes_severity(ghes->estatus->error_severity) <=
+               if (ghes_severity(estatus->error_severity) <=
                    GHES_SEV_CORRECTED)
                        pfx = KERN_WARNING HW_ERR;
                else
                        pfx = KERN_ERR HW_ERR;
        }
-       if (__ratelimit(&ratelimit)) {
-               printk(
-       "%s""Hardware error from APEI Generic Hardware Error Source: %d\n",
-       pfx, ghes->generic->header.source_id);
-               apei_estatus_print(pfx, ghes->estatus);
+       printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n",
+              pfx, generic->header.source_id);
+       apei_estatus_print(pfx, estatus);
+}
+
+static int ghes_print_estatus(const char *pfx,
+                             const struct acpi_hest_generic *generic,
+                             const struct acpi_hest_generic_status *estatus)
+{
+       /* Not more than 2 messages every 5 seconds */
+       static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2);
+       static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5*HZ, 2);
+       struct ratelimit_state *ratelimit;
+
+       if (ghes_severity(estatus->error_severity) <= GHES_SEV_CORRECTED)
+               ratelimit = &ratelimit_corrected;
+       else
+               ratelimit = &ratelimit_uncorrected;
+       if (__ratelimit(ratelimit)) {
+               __ghes_print_estatus(pfx, generic, estatus);
+               return 1;
        }
+       return 0;
+}
+
+/*
+ * GHES error status reporting throttle, to report more kinds of
+ * errors, instead of just most frequently occurred errors.
+ */
+static int ghes_estatus_cached(struct acpi_hest_generic_status *estatus)
+{
+       u32 len;
+       int i, cached = 0;
+       unsigned long long now;
+       struct ghes_estatus_cache *cache;
+       struct acpi_hest_generic_status *cache_estatus;
+
+       len = apei_estatus_len(estatus);
+       rcu_read_lock();
+       for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
+               cache = rcu_dereference(ghes_estatus_caches[i]);
+               if (cache == NULL)
+                       continue;
+               if (len != cache->estatus_len)
+                       continue;
+               cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
+               if (memcmp(estatus, cache_estatus, len))
+                       continue;
+               atomic_inc(&cache->count);
+               now = sched_clock();
+               if (now - cache->time_in < GHES_ESTATUS_IN_CACHE_MAX_NSEC)
+                       cached = 1;
+               break;
+       }
+       rcu_read_unlock();
+       return cached;
+}
+
+static struct ghes_estatus_cache *ghes_estatus_cache_alloc(
+       struct acpi_hest_generic *generic,
+       struct acpi_hest_generic_status *estatus)
+{
+       int alloced;
+       u32 len, cache_len;
+       struct ghes_estatus_cache *cache;
+       struct acpi_hest_generic_status *cache_estatus;
+
+       alloced = atomic_add_return(1, &ghes_estatus_cache_alloced);
+       if (alloced > GHES_ESTATUS_CACHE_ALLOCED_MAX) {
+               atomic_dec(&ghes_estatus_cache_alloced);
+               return NULL;
+       }
+       len = apei_estatus_len(estatus);
+       cache_len = GHES_ESTATUS_CACHE_LEN(len);
+       cache = (void *)gen_pool_alloc(ghes_estatus_pool, cache_len);
+       if (!cache) {
+               atomic_dec(&ghes_estatus_cache_alloced);
+               return NULL;
+       }
+       cache_estatus = GHES_ESTATUS_FROM_CACHE(cache);
+       memcpy(cache_estatus, estatus, len);
+       cache->estatus_len = len;
+       atomic_set(&cache->count, 0);
+       cache->generic = generic;
+       cache->time_in = sched_clock();
+       return cache;
+}
+
+static void ghes_estatus_cache_free(struct ghes_estatus_cache *cache)
+{
+       u32 len;
+
+       len = apei_estatus_len(GHES_ESTATUS_FROM_CACHE(cache));
+       len = GHES_ESTATUS_CACHE_LEN(len);
+       gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len);
+       atomic_dec(&ghes_estatus_cache_alloced);
+}
+
+static void ghes_estatus_cache_rcu_free(struct rcu_head *head)
+{
+       struct ghes_estatus_cache *cache;
+
+       cache = container_of(head, struct ghes_estatus_cache, rcu);
+       ghes_estatus_cache_free(cache);
+}
+
+static void ghes_estatus_cache_add(
+       struct acpi_hest_generic *generic,
+       struct acpi_hest_generic_status *estatus)
+{
+       int i, slot = -1, count;
+       unsigned long long now, duration, period, max_period = 0;
+       struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache;
+
+       new_cache = ghes_estatus_cache_alloc(generic, estatus);
+       if (new_cache == NULL)
+               return;
+       rcu_read_lock();
+       now = sched_clock();
+       for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
+               cache = rcu_dereference(ghes_estatus_caches[i]);
+               if (cache == NULL) {
+                       slot = i;
+                       slot_cache = NULL;
+                       break;
+               }
+               duration = now - cache->time_in;
+               if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) {
+                       slot = i;
+                       slot_cache = cache;
+                       break;
+               }
+               count = atomic_read(&cache->count);
+               period = duration;
+               do_div(period, (count + 1));
+               if (period > max_period) {
+                       max_period = period;
+                       slot = i;
+                       slot_cache = cache;
+               }
+       }
+       /* new_cache must be put into array after its contents are written */
+       smp_wmb();
+       if (slot != -1 && cmpxchg(ghes_estatus_caches + slot,
+                                 slot_cache, new_cache) == slot_cache) {
+               if (slot_cache)
+                       call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free);
+       } else
+               ghes_estatus_cache_free(new_cache);
+       rcu_read_unlock();
 }
 
 static int ghes_proc(struct ghes *ghes)
@@ -387,9 +648,11 @@ static int ghes_proc(struct ghes *ghes)
        rc = ghes_read_estatus(ghes, 0);
        if (rc)
                goto out;
-       ghes_print_estatus(NULL, ghes);
-       ghes_do_proc(ghes);
-
+       if (!ghes_estatus_cached(ghes->estatus)) {
+               if (ghes_print_estatus(NULL, ghes->generic, ghes->estatus))
+                       ghes_estatus_cache_add(ghes->generic, ghes->estatus);
+       }
+       ghes_do_proc(ghes->estatus);
 out:
        ghes_clear_estatus(ghes);
        return 0;
@@ -447,6 +710,45 @@ static int ghes_notify_sci(struct notifier_block *this,
        return ret;
 }
 
+static void ghes_proc_in_irq(struct irq_work *irq_work)
+{
+       struct llist_node *llnode, *next, *tail = NULL;
+       struct ghes_estatus_node *estatus_node;
+       struct acpi_hest_generic *generic;
+       struct acpi_hest_generic_status *estatus;
+       u32 len, node_len;
+
+       /*
+        * Because the time order of estatus in list is reversed,
+        * revert it back to proper order.
+        */
+       llnode = llist_del_all(&ghes_estatus_llist);
+       while (llnode) {
+               next = llnode->next;
+               llnode->next = tail;
+               tail = llnode;
+               llnode = next;
+       }
+       llnode = tail;
+       while (llnode) {
+               next = llnode->next;
+               estatus_node = llist_entry(llnode, struct ghes_estatus_node,
+                                          llnode);
+               estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
+               len = apei_estatus_len(estatus);
+               node_len = GHES_ESTATUS_NODE_LEN(len);
+               ghes_do_proc(estatus);
+               if (!ghes_estatus_cached(estatus)) {
+                       generic = estatus_node->generic;
+                       if (ghes_print_estatus(NULL, generic, estatus))
+                               ghes_estatus_cache_add(generic, estatus);
+               }
+               gen_pool_free(ghes_estatus_pool, (unsigned long)estatus_node,
+                             node_len);
+               llnode = next;
+       }
+}
+
 static int ghes_notify_nmi(struct notifier_block *this,
                                  unsigned long cmd, void *data)
 {
@@ -476,7 +778,8 @@ static int ghes_notify_nmi(struct notifier_block *this,
 
        if (sev_global >= GHES_SEV_PANIC) {
                oops_begin();
-               ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global);
+               __ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global->generic,
+                                    ghes_global->estatus);
                /* reboot to log the error! */
                if (panic_timeout == 0)
                        panic_timeout = ghes_panic_timeout;
@@ -484,12 +787,34 @@ static int ghes_notify_nmi(struct notifier_block *this,
        }
 
        list_for_each_entry_rcu(ghes, &ghes_nmi, list) {
+#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+               u32 len, node_len;
+               struct ghes_estatus_node *estatus_node;
+               struct acpi_hest_generic_status *estatus;
+#endif
                if (!(ghes->flags & GHES_TO_CLEAR))
                        continue;
-               /* Do not print estatus because printk is not NMI safe */
-               ghes_do_proc(ghes);
+#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+               if (ghes_estatus_cached(ghes->estatus))
+                       goto next;
+               /* Save estatus for further processing in IRQ context */
+               len = apei_estatus_len(ghes->estatus);
+               node_len = GHES_ESTATUS_NODE_LEN(len);
+               estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool,
+                                                     node_len);
+               if (estatus_node) {
+                       estatus_node->generic = ghes->generic;
+                       estatus = GHES_ESTATUS_FROM_NODE(estatus_node);
+                       memcpy(estatus, ghes->estatus, len);
+                       llist_add(&estatus_node->llnode, &ghes_estatus_llist);
+               }
+next:
+#endif
                ghes_clear_estatus(ghes);
        }
+#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+       irq_work_queue(&ghes_proc_irq_work);
+#endif
 
 out:
        raw_spin_unlock(&ghes_nmi_lock);
@@ -504,10 +829,26 @@ static struct notifier_block ghes_notifier_nmi = {
        .notifier_call = ghes_notify_nmi,
 };
 
+static unsigned long ghes_esource_prealloc_size(
+       const struct acpi_hest_generic *generic)
+{
+       unsigned long block_length, prealloc_records, prealloc_size;
+
+       block_length = min_t(unsigned long, generic->error_block_length,
+                            GHES_ESTATUS_MAX_SIZE);
+       prealloc_records = max_t(unsigned long,
+                                generic->records_to_preallocate, 1);
+       prealloc_size = min_t(unsigned long, block_length * prealloc_records,
+                             GHES_ESOURCE_PREALLOC_MAX_SIZE);
+
+       return prealloc_size;
+}
+
 static int __devinit ghes_probe(struct platform_device *ghes_dev)
 {
        struct acpi_hest_generic *generic;
        struct ghes *ghes = NULL;
+       unsigned long len;
        int rc = -EINVAL;
 
        generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data;
@@ -573,6 +914,8 @@ static int __devinit ghes_probe(struct platform_device *ghes_dev)
                mutex_unlock(&ghes_list_mutex);
                break;
        case ACPI_HEST_NOTIFY_NMI:
+               len = ghes_esource_prealloc_size(generic);
+               ghes_estatus_pool_expand(len);
                mutex_lock(&ghes_list_mutex);
                if (list_empty(&ghes_nmi))
                        register_die_notifier(&ghes_notifier_nmi);
@@ -597,6 +940,7 @@ static int __devexit ghes_remove(struct platform_device *ghes_dev)
 {
        struct ghes *ghes;
        struct acpi_hest_generic *generic;
+       unsigned long len;
 
        ghes = platform_get_drvdata(ghes_dev);
        generic = ghes->generic;
@@ -627,6 +971,8 @@ static int __devexit ghes_remove(struct platform_device *ghes_dev)
                 * freed after NMI handler finishes.
                 */
                synchronize_rcu();
+               len = ghes_esource_prealloc_size(generic);
+               ghes_estatus_pool_shrink(len);
                break;
        default:
                BUG();
@@ -662,15 +1008,43 @@ static int __init ghes_init(void)
                return -EINVAL;
        }
 
+       if (ghes_disable) {
+               pr_info(GHES_PFX "GHES is not enabled!\n");
+               return -EINVAL;
+       }
+
+       init_irq_work(&ghes_proc_irq_work, ghes_proc_in_irq);
+
        rc = ghes_ioremap_init();
        if (rc)
                goto err;
 
-       rc = platform_driver_register(&ghes_platform_driver);
+       rc = ghes_estatus_pool_init();
        if (rc)
                goto err_ioremap_exit;
 
+       rc = ghes_estatus_pool_expand(GHES_ESTATUS_CACHE_AVG_SIZE *
+                                     GHES_ESTATUS_CACHE_ALLOCED_MAX);
+       if (rc)
+               goto err_pool_exit;
+
+       rc = platform_driver_register(&ghes_platform_driver);
+       if (rc)
+               goto err_pool_exit;
+
+       rc = apei_osc_setup();
+       if (rc == 0 && osc_sb_apei_support_acked)
+               pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit and WHEA _OSC.\n");
+       else if (rc == 0 && !osc_sb_apei_support_acked)
+               pr_info(GHES_PFX "APEI firmware first mode is enabled by WHEA _OSC.\n");
+       else if (rc && osc_sb_apei_support_acked)
+               pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n");
+       else
+               pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n");
+
        return 0;
+err_pool_exit:
+       ghes_estatus_pool_exit();
 err_ioremap_exit:
        ghes_ioremap_exit();
 err:
@@ -680,6 +1054,7 @@ err:
 static void __exit ghes_exit(void)
 {
        platform_driver_unregister(&ghes_platform_driver);
+       ghes_estatus_pool_exit();
        ghes_ioremap_exit();
 }
 
index 181bc2f7bb7411a4b300c23c2450ad79ae129a4b..05fee06f4d6e03019a3bc55bd3595b8967304669 100644 (file)
@@ -231,16 +231,17 @@ void __init acpi_hest_init(void)
                goto err;
        }
 
-       rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count);
-       if (rc)
-               goto err;
-
-       rc = hest_ghes_dev_register(ghes_count);
-       if (!rc) {
-               pr_info(HEST_PFX "Table parsing has been initialized.\n");
-               return;
+       if (!ghes_disable) {
+               rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count);
+               if (rc)
+                       goto err;
+               rc = hest_ghes_dev_register(ghes_count);
+               if (rc)
+                       goto err;
        }
 
+       pr_info(HEST_PFX "Table parsing has been initialized.\n");
+       return;
 err:
        hest_disable = 1;
 }
index 2c661353e8f26145dffffbc5339a11d22272acea..7711d94a04091442fcf7bf261c09d7db93186ebc 100644 (file)
@@ -55,6 +55,9 @@
 #define ACPI_BATTERY_NOTIFY_INFO       0x81
 #define ACPI_BATTERY_NOTIFY_THRESHOLD   0x82
 
+/* Battery power unit: 0 means mW, 1 means mA */
+#define ACPI_BATTERY_POWER_UNIT_MA     1
+
 #define _COMPONENT             ACPI_BATTERY_COMPONENT
 
 ACPI_MODULE_NAME("battery");
@@ -91,16 +94,12 @@ MODULE_DEVICE_TABLE(acpi, battery_device_ids);
 enum {
        ACPI_BATTERY_ALARM_PRESENT,
        ACPI_BATTERY_XINFO_PRESENT,
-       /* For buggy DSDTs that report negative 16-bit values for either
-        * charging or discharging current and/or report 0 as 65536
-        * due to bad math.
-        */
-       ACPI_BATTERY_QUIRK_SIGNED16_CURRENT,
        ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY,
 };
 
 struct acpi_battery {
        struct mutex lock;
+       struct mutex sysfs_lock;
        struct power_supply bat;
        struct acpi_device *device;
        struct notifier_block pm_nb;
@@ -301,7 +300,8 @@ static enum power_supply_property energy_battery_props[] = {
 #ifdef CONFIG_ACPI_PROCFS_POWER
 inline char *acpi_battery_units(struct acpi_battery *battery)
 {
-       return (battery->power_unit)?"mA":"mW";
+       return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ?
+               "mA" : "mW";
 }
 #endif
 
@@ -461,9 +461,17 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
        battery->update_time = jiffies;
        kfree(buffer.pointer);
 
-       if (test_bit(ACPI_BATTERY_QUIRK_SIGNED16_CURRENT, &battery->flags) &&
-           battery->rate_now != -1)
+       /* For buggy DSDTs that report negative 16-bit values for either
+        * charging or discharging current and/or report 0 as 65536
+        * due to bad math.
+        */
+       if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA &&
+               battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
+               (s16)(battery->rate_now) < 0) {
                battery->rate_now = abs((s16)battery->rate_now);
+               printk_once(KERN_WARNING FW_BUG "battery: (dis)charge rate"
+                       " invalid.\n");
+       }
 
        if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)
            && battery->capacity_now >= 0 && battery->capacity_now <= 100)
@@ -544,7 +552,7 @@ static int sysfs_add_battery(struct acpi_battery *battery)
 {
        int result;
 
-       if (battery->power_unit) {
+       if (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) {
                battery->bat.properties = charge_battery_props;
                battery->bat.num_properties =
                        ARRAY_SIZE(charge_battery_props);
@@ -566,18 +574,16 @@ static int sysfs_add_battery(struct acpi_battery *battery)
 
 static void sysfs_remove_battery(struct acpi_battery *battery)
 {
-       if (!battery->bat.dev)
+       mutex_lock(&battery->sysfs_lock);
+       if (!battery->bat.dev) {
+               mutex_unlock(&battery->sysfs_lock);
                return;
+       }
+
        device_remove_file(battery->bat.dev, &alarm_attr);
        power_supply_unregister(&battery->bat);
        battery->bat.dev = NULL;
-}
-
-static void acpi_battery_quirks(struct acpi_battery *battery)
-{
-       if (dmi_name_in_vendors("Acer") && battery->power_unit) {
-               set_bit(ACPI_BATTERY_QUIRK_SIGNED16_CURRENT, &battery->flags);
-       }
+       mutex_unlock(&battery->sysfs_lock);
 }
 
 /*
@@ -592,7 +598,7 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
  *
  * Handle this correctly so that they won't break userspace.
  */
-static void acpi_battery_quirks2(struct acpi_battery *battery)
+static void acpi_battery_quirks(struct acpi_battery *battery)
 {
        if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags))
                return ;
@@ -623,13 +629,15 @@ static int acpi_battery_update(struct acpi_battery *battery)
                result = acpi_battery_get_info(battery);
                if (result)
                        return result;
-               acpi_battery_quirks(battery);
                acpi_battery_init_alarm(battery);
        }
-       if (!battery->bat.dev)
-               sysfs_add_battery(battery);
+       if (!battery->bat.dev) {
+               result = sysfs_add_battery(battery);
+               if (result)
+                       return result;
+       }
        result = acpi_battery_get_state(battery);
-       acpi_battery_quirks2(battery);
+       acpi_battery_quirks(battery);
        return result;
 }
 
@@ -863,7 +871,7 @@ DECLARE_FILE_FUNCTIONS(alarm);
                }, \
        }
 
-static struct battery_file {
+static const struct battery_file {
        struct file_operations ops;
        mode_t mode;
        const char *name;
@@ -948,9 +956,12 @@ static int battery_notify(struct notifier_block *nb,
        struct acpi_battery *battery = container_of(nb, struct acpi_battery,
                                                    pm_nb);
        switch (mode) {
+       case PM_POST_HIBERNATION:
        case PM_POST_SUSPEND:
-               sysfs_remove_battery(battery);
-               sysfs_add_battery(battery);
+               if (battery->bat.dev) {
+                       sysfs_remove_battery(battery);
+                       sysfs_add_battery(battery);
+               }
                break;
        }
 
@@ -972,28 +983,38 @@ static int acpi_battery_add(struct acpi_device *device)
        strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
        device->driver_data = battery;
        mutex_init(&battery->lock);
+       mutex_init(&battery->sysfs_lock);
        if (ACPI_SUCCESS(acpi_get_handle(battery->device->handle,
                        "_BIX", &handle)))
                set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags);
-       acpi_battery_update(battery);
+       result = acpi_battery_update(battery);
+       if (result)
+               goto fail;
 #ifdef CONFIG_ACPI_PROCFS_POWER
        result = acpi_battery_add_fs(device);
 #endif
-       if (!result) {
-               printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
-                       ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
-                       device->status.battery_present ? "present" : "absent");
-       } else {
+       if (result) {
 #ifdef CONFIG_ACPI_PROCFS_POWER
                acpi_battery_remove_fs(device);
 #endif
-               kfree(battery);
+               goto fail;
        }
 
+       printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
+               ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
+               device->status.battery_present ? "present" : "absent");
+
        battery->pm_nb.notifier_call = battery_notify;
        register_pm_notifier(&battery->pm_nb);
 
        return result;
+
+fail:
+       sysfs_remove_battery(battery);
+       mutex_destroy(&battery->lock);
+       mutex_destroy(&battery->sysfs_lock);
+       kfree(battery);
+       return result;
 }
 
 static int acpi_battery_remove(struct acpi_device *device, int type)
@@ -1009,6 +1030,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type)
 #endif
        sysfs_remove_battery(battery);
        mutex_destroy(&battery->lock);
+       mutex_destroy(&battery->sysfs_lock);
        kfree(battery);
        return 0;
 }
index d1e06c182cdba8ee46d404dc7cdb95571e9fe893..437ddbf0c49abbd521dde72eae7011c5e4d3672f 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/pci.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
+#include <acpi/apei.h>
 #include <linux/dmi.h>
 #include <linux/suspend.h>
 
@@ -519,6 +520,7 @@ out_kfree:
 }
 EXPORT_SYMBOL(acpi_run_osc);
 
+bool osc_sb_apei_support_acked;
 static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48";
 static void acpi_bus_osc_support(void)
 {
@@ -541,11 +543,19 @@ static void acpi_bus_osc_support(void)
 #if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE)
        capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_PPC_OST_SUPPORT;
 #endif
+
+       if (!ghes_disable)
+               capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_APEI_SUPPORT;
        if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle)))
                return;
-       if (ACPI_SUCCESS(acpi_run_osc(handle, &context)))
+       if (ACPI_SUCCESS(acpi_run_osc(handle, &context))) {
+               u32 *capbuf_ret = context.ret.pointer;
+               if (context.ret.length > OSC_SUPPORT_TYPE)
+                       osc_sb_apei_support_acked =
+                               capbuf_ret[OSC_SUPPORT_TYPE] & OSC_SB_APEI_SUPPORT;
                kfree(context.ret.pointer);
-       /* do we need to check the returned cap? Sounds no */
+       }
+       /* do we need to check other returned cap? Sounds no */
 }
 
 /* --------------------------------------------------------------------------
index 1864ad3cf89590267919eebe6c62f62ebba84cb3..19a61136d8488460c1d7be2f96bb2c8dc88d0500 100644 (file)
@@ -77,7 +77,7 @@ struct dock_dependent_device {
        struct list_head list;
        struct list_head hotplug_list;
        acpi_handle handle;
-       struct acpi_dock_ops *ops;
+       const struct acpi_dock_ops *ops;
        void *context;
 };
 
@@ -589,7 +589,7 @@ EXPORT_SYMBOL_GPL(unregister_dock_notifier);
  * the dock driver after _DCK is executed.
  */
 int
-register_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops,
+register_hotplug_dock_device(acpi_handle handle, const struct acpi_dock_ops *ops,
                             void *context)
 {
        struct dock_dependent_device *dd;
index 05b44201a61476988475fce1fea5d6b817e0565d..22f918bacd35b0186af2328ec68d19b0567b08b9 100644 (file)
@@ -92,7 +92,7 @@ static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf,
        return count;
 }
 
-static struct file_operations acpi_ec_io_ops = {
+static const struct file_operations acpi_ec_io_ops = {
        .owner = THIS_MODULE,
        .open  = acpi_ec_open_io,
        .read  = acpi_ec_read_io,
index 467479f07c1fd35932390cba80ffe90add0dba65..0f0356ca1a9e99c24bd17f63e910538e63fc7a79 100644 (file)
@@ -110,7 +110,7 @@ fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
        return result;
 }
 
-static struct thermal_cooling_device_ops fan_cooling_ops = {
+static const struct thermal_cooling_device_ops fan_cooling_ops = {
        .get_max_state = fan_get_max_state,
        .get_cur_state = fan_get_cur_state,
        .set_cur_state = fan_set_cur_state,
index 372f9b70f7f4dc98e2985532fb19707861c920a8..fa32f584229f47f8965d56f6e4d61054796d8d39 100644 (file)
@@ -155,7 +155,7 @@ static u32 acpi_osi_handler(acpi_string interface, u32 supported)
 {
        if (!strcmp("Linux", interface)) {
 
-               printk(KERN_NOTICE FW_BUG PREFIX
+               printk_once(KERN_NOTICE FW_BUG PREFIX
                        "BIOS _OSI(Linux) query %s%s\n",
                        osi_linux.enable ? "honored" : "ignored",
                        osi_linux.cmdline ? " via cmdline" :
@@ -237,8 +237,23 @@ void acpi_os_vprintf(const char *fmt, va_list args)
 #endif
 }
 
+#ifdef CONFIG_KEXEC
+static unsigned long acpi_rsdp;
+static int __init setup_acpi_rsdp(char *arg)
+{
+       acpi_rsdp = simple_strtoul(arg, NULL, 16);
+       return 0;
+}
+early_param("acpi_rsdp", setup_acpi_rsdp);
+#endif
+
 acpi_physical_address __init acpi_os_get_root_pointer(void)
 {
+#ifdef CONFIG_KEXEC
+       if (acpi_rsdp)
+               return acpi_rsdp;
+#endif
+
        if (efi_enabled) {
                if (efi.acpi20 != EFI_INVALID_TABLE_ADDR)
                        return efi.acpi20;
@@ -1083,7 +1098,13 @@ struct osi_setup_entry {
        bool enable;
 };
 
-static struct osi_setup_entry __initdata osi_setup_entries[OSI_STRING_ENTRIES_MAX];
+static struct osi_setup_entry __initdata
+               osi_setup_entries[OSI_STRING_ENTRIES_MAX] = {
+       {"Module Device", true},
+       {"Processor Device", true},
+       {"3.0 _SCP Extensions", true},
+       {"Processor Aggregator Device", true},
+};
 
 void __init acpi_osi_setup(char *str)
 {
index f907cfbfa13c89cc0d173161b14e4f176d62296f..7f9eba9a0b02ce98a9a7bc9aa5ecf865ece618bf 100644 (file)
@@ -303,6 +303,61 @@ void acpi_pci_irq_del_prt(struct pci_bus *bus)
 /* --------------------------------------------------------------------------
                           PCI Interrupt Routing Support
    -------------------------------------------------------------------------- */
+#ifdef CONFIG_X86_IO_APIC
+extern int noioapicquirk;
+extern int noioapicreroute;
+
+static int bridge_has_boot_interrupt_variant(struct pci_bus *bus)
+{
+       struct pci_bus *bus_it;
+
+       for (bus_it = bus ; bus_it ; bus_it = bus_it->parent) {
+               if (!bus_it->self)
+                       return 0;
+               if (bus_it->self->irq_reroute_variant)
+                       return bus_it->self->irq_reroute_variant;
+       }
+       return 0;
+}
+
+/*
+ * Some chipsets (e.g. Intel 6700PXH) generate a legacy INTx when the IRQ
+ * entry in the chipset's IO-APIC is masked (as, e.g. the RT kernel does
+ * during interrupt handling). When this INTx generation cannot be disabled,
+ * we reroute these interrupts to their legacy equivalent to get rid of
+ * spurious interrupts.
+ */
+static int acpi_reroute_boot_interrupt(struct pci_dev *dev,
+                                      struct acpi_prt_entry *entry)
+{
+       if (noioapicquirk || noioapicreroute) {
+               return 0;
+       } else {
+               switch (bridge_has_boot_interrupt_variant(dev->bus)) {
+               case 0:
+                       /* no rerouting necessary */
+                       return 0;
+               case INTEL_IRQ_REROUTE_VARIANT:
+                       /*
+                        * Remap according to INTx routing table in 6700PXH
+                        * specs, intel order number 302628-002, section
+                        * 2.15.2. Other chipsets (80332, ...) have the same
+                        * mapping and are handled here as well.
+                        */
+                       dev_info(&dev->dev, "PCI IRQ %d -> rerouted to legacy "
+                                "IRQ %d\n", entry->index,
+                                (entry->index % 4) + 16);
+                       entry->index = (entry->index % 4) + 16;
+                       return 1;
+               default:
+                       dev_warn(&dev->dev, "Cannot reroute IRQ %d to legacy "
+                                "IRQ: unknown mapping\n", entry->index);
+                       return -1;
+               }
+       }
+}
+#endif /* CONFIG_X86_IO_APIC */
+
 static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
 {
        struct acpi_prt_entry *entry;
@@ -311,6 +366,9 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin)
 
        entry = acpi_pci_irq_find_prt_entry(dev, pin);
        if (entry) {
+#ifdef CONFIG_X86_IO_APIC
+               acpi_reroute_boot_interrupt(dev, entry);
+#endif /* CONFIG_X86_IO_APIC */
                ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %s[%c] _PRT entry\n",
                                  pci_name(dev), pin_name(pin)));
                return entry;
index d06078d660adecaeb1113bd074cf7b8945d2008a..2672c798272fa1f419c98aeba0540fd39601f163 100644 (file)
@@ -485,7 +485,8 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
                root->secondary.end = 0xFF;
                printk(KERN_WARNING FW_BUG PREFIX
                       "no secondary bus range in _CRS\n");
-               status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN,                                               NULL, &bus);
+               status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN,
+                                              NULL, &bus);
                if (ACPI_SUCCESS(status))
                        root->secondary.start = bus;
                else if (status == AE_NOT_FOUND)
index 79cb6533289456c4f43b92dda5d7059a27345afa..870550d6a4bf30a04208a4439533e85cf9ca7281 100644 (file)
@@ -244,7 +244,7 @@ processor_set_cur_state(struct thermal_cooling_device *cdev,
        return result;
 }
 
-struct thermal_cooling_device_ops processor_cooling_ops = {
+const struct thermal_cooling_device_ops processor_cooling_ops = {
        .get_max_state = processor_get_max_state,
        .get_cur_state = processor_get_cur_state,
        .set_cur_state = processor_set_cur_state,
index 50658ff887d99a51f51bca23be2ba8fee4470281..6e36d0c0057c1303464aca74ba0a2b1d9ad3bc25 100644 (file)
@@ -130,6 +130,9 @@ struct acpi_sbs {
 
 #define to_acpi_sbs(x) container_of(x, struct acpi_sbs, charger)
 
+static int acpi_sbs_remove(struct acpi_device *device, int type);
+static int acpi_battery_get_state(struct acpi_battery *battery);
+
 static inline int battery_scale(int log)
 {
        int scale = 1;
@@ -195,6 +198,8 @@ static int acpi_sbs_battery_get_property(struct power_supply *psy,
 
        if ((!battery->present) && psp != POWER_SUPPLY_PROP_PRESENT)
                return -ENODEV;
+
+       acpi_battery_get_state(battery);
        switch (psp) {
        case POWER_SUPPLY_PROP_STATUS:
                if (battery->rate_now < 0)
@@ -225,11 +230,17 @@ static int acpi_sbs_battery_get_property(struct power_supply *psy,
        case POWER_SUPPLY_PROP_POWER_NOW:
                val->intval = abs(battery->rate_now) *
                                acpi_battery_ipscale(battery) * 1000;
+               val->intval *= (acpi_battery_mode(battery)) ?
+                               (battery->voltage_now *
+                               acpi_battery_vscale(battery) / 1000) : 1;
                break;
        case POWER_SUPPLY_PROP_CURRENT_AVG:
        case POWER_SUPPLY_PROP_POWER_AVG:
                val->intval = abs(battery->rate_avg) *
                                acpi_battery_ipscale(battery) * 1000;
+               val->intval *= (acpi_battery_mode(battery)) ?
+                               (battery->voltage_now *
+                               acpi_battery_vscale(battery) / 1000) : 1;
                break;
        case POWER_SUPPLY_PROP_CAPACITY:
                val->intval = battery->state_of_charge;
@@ -903,8 +914,6 @@ static void acpi_sbs_callback(void *context)
        }
 }
 
-static int acpi_sbs_remove(struct acpi_device *device, int type);
-
 static int acpi_sbs_add(struct acpi_device *device)
 {
        struct acpi_sbs *sbs;
index 6c949602cbd111d18753217ea0ee4784054ec008..3ed80b2ca9078b18951d69cd00928f3dbcc53fbd 100644 (file)
@@ -428,6 +428,22 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
                DMI_MATCH(DMI_PRODUCT_NAME, "1000 Series"),
                },
        },
+       {
+       .callback = init_old_suspend_ordering,
+       .ident = "Asus A8N-SLI DELUXE",
+       .matches = {
+               DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+               DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI DELUXE"),
+               },
+       },
+       {
+       .callback = init_old_suspend_ordering,
+       .ident = "Asus A8N-SLI Premium",
+       .matches = {
+               DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+               DMI_MATCH(DMI_BOARD_NAME, "A8N-SLI Premium"),
+               },
+       },
        {},
 };
 #endif /* CONFIG_SUSPEND */
index 77255f250dbb7c2afe29aabc640d5227fddf7d08..c538d0ef10ff648de4140eb54f6f7c8c3c7bc88f 100644 (file)
@@ -149,12 +149,12 @@ static int param_get_debug_level(char *buffer, const struct kernel_param *kp)
        return result;
 }
 
-static struct kernel_param_ops param_ops_debug_layer = {
+static const struct kernel_param_ops param_ops_debug_layer = {
        .set = param_set_uint,
        .get = param_get_debug_layer,
 };
 
-static struct kernel_param_ops param_ops_debug_level = {
+static const struct kernel_param_ops param_ops_debug_level = {
        .set = param_set_uint,
        .get = param_get_debug_level,
 };
index 2607e17b520f9b39a5d60f7bee508ba62b813ea0..48fbc647b1780965393c623546d208c3c2e63b35 100644 (file)
@@ -812,7 +812,7 @@ acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal,
                                thermal_zone_unbind_cooling_device);
 }
 
-static struct thermal_zone_device_ops acpi_thermal_zone_ops = {
+static const struct thermal_zone_device_ops acpi_thermal_zone_ops = {
        .bind = acpi_thermal_bind_cooling_device,
        .unbind = acpi_thermal_unbind_cooling_device,
        .get_temp = thermal_get_temp,
index ada4b4d9bdc8253784aba7929de8d70800895832..08a44b532f7c886dd45c703cb4cd159fb9d6e706 100644 (file)
@@ -307,7 +307,7 @@ video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long st
        return acpi_video_device_lcd_set_level(video, level);
 }
 
-static struct thermal_cooling_device_ops video_cooling_ops = {
+static const struct thermal_cooling_device_ops video_cooling_ops = {
        .get_max_state = video_get_max_state,
        .get_cur_state = video_get_cur_state,
        .set_cur_state = video_set_cur_state,
index e0a5b555cee17f7c0784dc746645ebe85c8f4d65..bb7c5f1085ccdfa997671520afc87c4f887eed39 100644 (file)
@@ -218,12 +218,12 @@ static void ata_acpi_dev_uevent(acpi_handle handle, u32 event, void *data)
        ata_acpi_uevent(dev->link->ap, dev, event);
 }
 
-static struct acpi_dock_ops ata_acpi_dev_dock_ops = {
+static const struct acpi_dock_ops ata_acpi_dev_dock_ops = {
        .handler = ata_acpi_dev_notify_dock,
        .uevent = ata_acpi_dev_uevent,
 };
 
-static struct acpi_dock_ops ata_acpi_ap_dock_ops = {
+static const struct acpi_dock_ops ata_acpi_ap_dock_ops = {
        .handler = ata_acpi_ap_notify_dock,
        .uevent = ata_acpi_ap_uevent,
 };
index b89fffc1d777b277abd7bac4221b0fbc98e265ff..33e1bed68fddf771ae9b12a716114215b59b5f68 100644 (file)
@@ -166,7 +166,7 @@ static int create_path(const char *nodepath)
 {
        char *path;
        char *s;
-       int err;
+       int err = 0;
 
        /* parent directories do not exist, create them */
        path = kstrdup(nodepath, GFP_KERNEL);
index be8714aa9dd6cbfcf46fc2216f12d922d48b6b3d..e18566a0fedd4c9f7492eab168ffed2bc8d36932 100644 (file)
@@ -80,7 +80,6 @@ static void genpd_set_active(struct generic_pm_domain *genpd)
 int pm_genpd_poweron(struct generic_pm_domain *genpd)
 {
        struct generic_pm_domain *parent = genpd->parent;
-       DEFINE_WAIT(wait);
        int ret = 0;
 
  start:
@@ -112,7 +111,7 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd)
        }
 
        if (genpd->power_on) {
-               int ret = genpd->power_on(genpd);
+               ret = genpd->power_on(genpd);
                if (ret)
                        goto out;
        }
index 8dc247c974affdc3432d4ec73087f76cd1889ce6..acb3f83b8079486e214dc23e315ca3f9f6f755c4 100644 (file)
@@ -226,11 +226,17 @@ static int rpm_idle(struct device *dev, int rpmflags)
                callback = NULL;
 
        if (callback) {
-               spin_unlock_irq(&dev->power.lock);
+               if (dev->power.irq_safe)
+                       spin_unlock(&dev->power.lock);
+               else
+                       spin_unlock_irq(&dev->power.lock);
 
                callback(dev);
 
-               spin_lock_irq(&dev->power.lock);
+               if (dev->power.irq_safe)
+                       spin_lock(&dev->power.lock);
+               else
+                       spin_lock_irq(&dev->power.lock);
        }
 
        dev->power.idle_notification = false;
index 49502bc5360aa70fe83d455388b3dde0efaa284c..423fd56bf6128edc9e49f72b49929cdcc14b2915 100644 (file)
@@ -616,5 +616,16 @@ config MSM_SMD_PKT
          Enables userspace clients to read and write to some packet SMD
          ports via device interface for MSM chipset.
 
+config TILE_SROM
+       bool "Character-device access via hypervisor to the Tilera SPI ROM"
+       depends on TILE
+       default y
+       ---help---
+         This device provides character-level read-write access
+         to the SROM, typically via the "0", "1", and "2" devices
+         in /dev/srom/.  The Tilera hypervisor makes the flash
+         device appear much like a simple EEPROM, and knows
+         how to partition a single ROM for multiple purposes.
+
 endmenu
 
index 7a00672bd85de7b7256855653bce7d3c04b0b33b..32762ba769c223d1eb7b9bf18afde58a3eaf1205 100644 (file)
@@ -63,3 +63,5 @@ obj-$(CONFIG_RAMOOPS)         += ramoops.o
 
 obj-$(CONFIG_JS_RTC)           += js-rtc.o
 js-rtc-y = rtc.o
+
+obj-$(CONFIG_TILE_SROM)                += tile-srom.o
index fca0c51bbc90c92bb605588840f5bdf9bfdc5c2f..810aff9e750fde8e28a16d6a81866c72a6dc5aa2 100644 (file)
@@ -147,6 +147,14 @@ static int __init ramoops_probe(struct platform_device *pdev)
        cxt->phys_addr = pdata->mem_address;
        cxt->record_size = pdata->record_size;
        cxt->dump_oops = pdata->dump_oops;
+       /*
+        * Update the module parameter variables as well so they are visible
+        * through /sys/module/ramoops/parameters/
+        */
+       mem_size = pdata->mem_size;
+       mem_address = pdata->mem_address;
+       record_size = pdata->record_size;
+       dump_oops = pdata->dump_oops;
 
        if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) {
                pr_err("request mem region failed\n");
diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c
new file mode 100644 (file)
index 0000000..cf3ee00
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * Copyright 2011 Tilera Corporation. 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, version 2.
+ *
+ *   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.
+ *
+ * SPI Flash ROM driver
+ *
+ * This source code is derived from code provided in "Linux Device
+ * Drivers, Third Edition", by Jonathan Corbet, Alessandro Rubini, and
+ * Greg Kroah-Hartman, published by O'Reilly Media, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>      /* printk() */
+#include <linux/slab.h>                /* kmalloc() */
+#include <linux/fs.h>          /* everything... */
+#include <linux/errno.h>       /* error codes */
+#include <linux/types.h>       /* size_t */
+#include <linux/proc_fs.h>
+#include <linux/fcntl.h>       /* O_ACCMODE */
+#include <linux/aio.h>
+#include <linux/pagemap.h>
+#include <linux/hugetlb.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <hv/hypervisor.h>
+#include <linux/ioctl.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <hv/drv_srom_intf.h>
+
+/*
+ * Size of our hypervisor I/O requests.  We break up large transfers
+ * so that we don't spend large uninterrupted spans of time in the
+ * hypervisor.  Erasing an SROM sector takes a significant fraction of
+ * a second, so if we allowed the user to, say, do one I/O to write the
+ * entire ROM, we'd get soft lockup timeouts, or worse.
+ */
+#define SROM_CHUNK_SIZE ((size_t)4096)
+
+/*
+ * When hypervisor is busy (e.g. erasing), poll the status periodically.
+ */
+
+/*
+ * Interval to poll the state in msec
+ */
+#define SROM_WAIT_TRY_INTERVAL 20
+
+/*
+ * Maximum times to poll the state
+ */
+#define SROM_MAX_WAIT_TRY_TIMES 1000
+
+struct srom_dev {
+       int hv_devhdl;                  /* Handle for hypervisor device */
+       u32 total_size;                 /* Size of this device */
+       u32 sector_size;                /* Size of a sector */
+       u32 page_size;                  /* Size of a page */
+       struct mutex lock;              /* Allow only one accessor at a time */
+};
+
+static int srom_major;                 /* Dynamic major by default */
+module_param(srom_major, int, 0);
+MODULE_AUTHOR("Tilera Corporation");
+MODULE_LICENSE("GPL");
+
+static int srom_devs;                  /* Number of SROM partitions */
+static struct cdev srom_cdev;
+static struct class *srom_class;
+static struct srom_dev *srom_devices;
+
+/*
+ * Handle calling the hypervisor and managing EAGAIN/EBUSY.
+ */
+
+static ssize_t _srom_read(int hv_devhdl, void *buf,
+                         loff_t off, size_t count)
+{
+       int retval, retries = SROM_MAX_WAIT_TRY_TIMES;
+       for (;;) {
+               retval = hv_dev_pread(hv_devhdl, 0, (HV_VirtAddr)buf,
+                                     count, off);
+               if (retval >= 0)
+                       return retval;
+               if (retval == HV_EAGAIN)
+                       continue;
+               if (retval == HV_EBUSY && --retries > 0) {
+                       msleep(SROM_WAIT_TRY_INTERVAL);
+                       continue;
+               }
+               pr_err("_srom_read: error %d\n", retval);
+               return -EIO;
+       }
+}
+
+static ssize_t _srom_write(int hv_devhdl, const void *buf,
+                          loff_t off, size_t count)
+{
+       int retval, retries = SROM_MAX_WAIT_TRY_TIMES;
+       for (;;) {
+               retval = hv_dev_pwrite(hv_devhdl, 0, (HV_VirtAddr)buf,
+                                      count, off);
+               if (retval >= 0)
+                       return retval;
+               if (retval == HV_EAGAIN)
+                       continue;
+               if (retval == HV_EBUSY && --retries > 0) {
+                       msleep(SROM_WAIT_TRY_INTERVAL);
+                       continue;
+               }
+               pr_err("_srom_write: error %d\n", retval);
+               return -EIO;
+       }
+}
+
+/**
+ * srom_open() - Device open routine.
+ * @inode: Inode for this device.
+ * @filp: File for this specific open of the device.
+ *
+ * Returns zero, or an error code.
+ */
+static int srom_open(struct inode *inode, struct file *filp)
+{
+       filp->private_data = &srom_devices[iminor(inode)];
+       return 0;
+}
+
+
+/**
+ * srom_release() - Device release routine.
+ * @inode: Inode for this device.
+ * @filp: File for this specific open of the device.
+ *
+ * Returns zero, or an error code.
+ */
+static int srom_release(struct inode *inode, struct file *filp)
+{
+       struct srom_dev *srom = filp->private_data;
+       char dummy;
+
+       /* Make sure we've flushed anything written to the ROM. */
+       mutex_lock(&srom->lock);
+       if (srom->hv_devhdl >= 0)
+               _srom_write(srom->hv_devhdl, &dummy, SROM_FLUSH_OFF, 1);
+       mutex_unlock(&srom->lock);
+
+       filp->private_data = NULL;
+
+       return 0;
+}
+
+
+/**
+ * srom_read() - Read data from the device.
+ * @filp: File for this specific open of the device.
+ * @buf: User's data buffer.
+ * @count: Number of bytes requested.
+ * @f_pos: File position.
+ *
+ * Returns number of bytes read, or an error code.
+ */
+static ssize_t srom_read(struct file *filp, char __user *buf,
+                        size_t count, loff_t *f_pos)
+{
+       int retval = 0;
+       void *kernbuf;
+       struct srom_dev *srom = filp->private_data;
+
+       kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL);
+       if (!kernbuf)
+               return -ENOMEM;
+
+       if (mutex_lock_interruptible(&srom->lock)) {
+               retval = -ERESTARTSYS;
+               kfree(kernbuf);
+               return retval;
+       }
+
+       while (count) {
+               int hv_retval;
+               int bytes_this_pass = min(count, SROM_CHUNK_SIZE);
+
+               hv_retval = _srom_read(srom->hv_devhdl, kernbuf,
+                                      *f_pos, bytes_this_pass);
+               if (hv_retval > 0) {
+                       if (copy_to_user(buf, kernbuf, hv_retval) != 0) {
+                               retval = -EFAULT;
+                               break;
+                       }
+               } else if (hv_retval <= 0) {
+                       if (retval == 0)
+                               retval = hv_retval;
+                       break;
+               }
+
+               retval += hv_retval;
+               *f_pos += hv_retval;
+               buf += hv_retval;
+               count -= hv_retval;
+       }
+
+       mutex_unlock(&srom->lock);
+       kfree(kernbuf);
+
+       return retval;
+}
+
+/**
+ * srom_write() - Write data to the device.
+ * @filp: File for this specific open of the device.
+ * @buf: User's data buffer.
+ * @count: Number of bytes requested.
+ * @f_pos: File position.
+ *
+ * Returns number of bytes written, or an error code.
+ */
+static ssize_t srom_write(struct file *filp, const char __user *buf,
+                         size_t count, loff_t *f_pos)
+{
+       int retval = 0;
+       void *kernbuf;
+       struct srom_dev *srom = filp->private_data;
+
+       kernbuf = kmalloc(SROM_CHUNK_SIZE, GFP_KERNEL);
+       if (!kernbuf)
+               return -ENOMEM;
+
+       if (mutex_lock_interruptible(&srom->lock)) {
+               retval = -ERESTARTSYS;
+               kfree(kernbuf);
+               return retval;
+       }
+
+       while (count) {
+               int hv_retval;
+               int bytes_this_pass = min(count, SROM_CHUNK_SIZE);
+
+               if (copy_from_user(kernbuf, buf, bytes_this_pass) != 0) {
+                       retval = -EFAULT;
+                       break;
+               }
+
+               hv_retval = _srom_write(srom->hv_devhdl, kernbuf,
+                                       *f_pos, bytes_this_pass);
+               if (hv_retval <= 0) {
+                       if (retval == 0)
+                               retval = hv_retval;
+                       break;
+               }
+
+               retval += hv_retval;
+               *f_pos += hv_retval;
+               buf += hv_retval;
+               count -= hv_retval;
+       }
+
+       mutex_unlock(&srom->lock);
+       kfree(kernbuf);
+
+       return retval;
+}
+
+/* Provide our own implementation so we can use srom->total_size. */
+loff_t srom_llseek(struct file *filp, loff_t offset, int origin)
+{
+       struct srom_dev *srom = filp->private_data;
+
+       if (mutex_lock_interruptible(&srom->lock))
+               return -ERESTARTSYS;
+
+       switch (origin) {
+       case SEEK_END:
+               offset += srom->total_size;
+               break;
+       case SEEK_CUR:
+               offset += filp->f_pos;
+               break;
+       }
+
+       if (offset < 0 || offset > srom->total_size) {
+               offset = -EINVAL;
+       } else {
+               filp->f_pos = offset;
+               filp->f_version = 0;
+       }
+
+       mutex_unlock(&srom->lock);
+
+       return offset;
+}
+
+static ssize_t total_show(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       struct srom_dev *srom = dev_get_drvdata(dev);
+       return sprintf(buf, "%u\n", srom->total_size);
+}
+
+static ssize_t sector_show(struct device *dev,
+                          struct device_attribute *attr, char *buf)
+{
+       struct srom_dev *srom = dev_get_drvdata(dev);
+       return sprintf(buf, "%u\n", srom->sector_size);
+}
+
+static ssize_t page_show(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+       struct srom_dev *srom = dev_get_drvdata(dev);
+       return sprintf(buf, "%u\n", srom->page_size);
+}
+
+static struct device_attribute srom_dev_attrs[] = {
+       __ATTR(total_size, S_IRUGO, total_show, NULL),
+       __ATTR(sector_size, S_IRUGO, sector_show, NULL),
+       __ATTR(page_size, S_IRUGO, page_show, NULL),
+       __ATTR_NULL
+};
+
+static char *srom_devnode(struct device *dev, mode_t *mode)
+{
+       *mode = S_IRUGO | S_IWUSR;
+       return kasprintf(GFP_KERNEL, "srom/%s", dev_name(dev));
+}
+
+/*
+ * The fops
+ */
+static const struct file_operations srom_fops = {
+       .owner =     THIS_MODULE,
+       .llseek =    srom_llseek,
+       .read =      srom_read,
+       .write =     srom_write,
+       .open =      srom_open,
+       .release =   srom_release,
+};
+
+/**
+ * srom_setup_minor() - Initialize per-minor information.
+ * @srom: Per-device SROM state.
+ * @index: Device to set up.
+ */
+static int srom_setup_minor(struct srom_dev *srom, int index)
+{
+       struct device *dev;
+       int devhdl = srom->hv_devhdl;
+
+       mutex_init(&srom->lock);
+
+       if (_srom_read(devhdl, &srom->total_size,
+                      SROM_TOTAL_SIZE_OFF, sizeof(srom->total_size)) < 0)
+               return -EIO;
+       if (_srom_read(devhdl, &srom->sector_size,
+                      SROM_SECTOR_SIZE_OFF, sizeof(srom->sector_size)) < 0)
+               return -EIO;
+       if (_srom_read(devhdl, &srom->page_size,
+                      SROM_PAGE_SIZE_OFF, sizeof(srom->page_size)) < 0)
+               return -EIO;
+
+       dev = device_create(srom_class, &platform_bus,
+                           MKDEV(srom_major, index), srom, "%d", index);
+       return IS_ERR(dev) ? PTR_ERR(dev) : 0;
+}
+
+/** srom_init() - Initialize the driver's module. */
+static int srom_init(void)
+{
+       int result, i;
+       dev_t dev = MKDEV(srom_major, 0);
+
+       /*
+        * Start with a plausible number of partitions; the krealloc() call
+        * below will yield about log(srom_devs) additional allocations.
+        */
+       srom_devices = kzalloc(4 * sizeof(struct srom_dev), GFP_KERNEL);
+
+       /* Discover the number of srom partitions. */
+       for (i = 0; ; i++) {
+               int devhdl;
+               char buf[20];
+               struct srom_dev *new_srom_devices =
+                       krealloc(srom_devices, (i+1) * sizeof(struct srom_dev),
+                                GFP_KERNEL | __GFP_ZERO);
+               if (!new_srom_devices) {
+                       result = -ENOMEM;
+                       goto fail_mem;
+               }
+               srom_devices = new_srom_devices;
+               sprintf(buf, "srom/0/%d", i);
+               devhdl = hv_dev_open((HV_VirtAddr)buf, 0);
+               if (devhdl < 0) {
+                       if (devhdl != HV_ENODEV)
+                               pr_notice("srom/%d: hv_dev_open failed: %d.\n",
+                                         i, devhdl);
+                       break;
+               }
+               srom_devices[i].hv_devhdl = devhdl;
+       }
+       srom_devs = i;
+
+       /* Bail out early if we have no partitions at all. */
+       if (srom_devs == 0) {
+               result = -ENODEV;
+               goto fail_mem;
+       }
+
+       /* Register our major, and accept a dynamic number. */
+       if (srom_major)
+               result = register_chrdev_region(dev, srom_devs, "srom");
+       else {
+               result = alloc_chrdev_region(&dev, 0, srom_devs, "srom");
+               srom_major = MAJOR(dev);
+       }
+       if (result < 0)
+               goto fail_mem;
+
+       /* Register a character device. */
+       cdev_init(&srom_cdev, &srom_fops);
+       srom_cdev.owner = THIS_MODULE;
+       srom_cdev.ops = &srom_fops;
+       result = cdev_add(&srom_cdev, dev, srom_devs);
+       if (result < 0)
+               goto fail_chrdev;
+
+       /* Create a sysfs class. */
+       srom_class = class_create(THIS_MODULE, "srom");
+       if (IS_ERR(srom_class)) {
+               result = PTR_ERR(srom_class);
+               goto fail_cdev;
+       }
+       srom_class->dev_attrs = srom_dev_attrs;
+       srom_class->devnode = srom_devnode;
+
+       /* Do per-partition initialization */
+       for (i = 0; i < srom_devs; i++) {
+               result = srom_setup_minor(srom_devices + i, i);
+               if (result < 0)
+                       goto fail_class;
+       }
+
+       return 0;
+
+fail_class:
+       for (i = 0; i < srom_devs; i++)
+               device_destroy(srom_class, MKDEV(srom_major, i));
+       class_destroy(srom_class);
+fail_cdev:
+       cdev_del(&srom_cdev);
+fail_chrdev:
+       unregister_chrdev_region(dev, srom_devs);
+fail_mem:
+       kfree(srom_devices);
+       return result;
+}
+
+/** srom_cleanup() - Clean up the driver's module. */
+static void srom_cleanup(void)
+{
+       int i;
+       for (i = 0; i < srom_devs; i++)
+               device_destroy(srom_class, MKDEV(srom_major, i));
+       class_destroy(srom_class);
+       cdev_del(&srom_cdev);
+       unregister_chrdev_region(MKDEV(srom_major, 0), srom_devs);
+       kfree(srom_devices);
+}
+
+module_init(srom_init);
+module_exit(srom_cleanup);
index 7fc2f108f4904debbc306664816fa8259d8d354b..3f4051a7c5a770440d8d0b32893c1885fa796480 100644 (file)
@@ -80,7 +80,7 @@ enum tis_defaults {
 static LIST_HEAD(tis_chips);
 static DEFINE_SPINLOCK(tis_lock);
 
-#ifdef CONFIG_PNP
+#if defined(CONFIG_PNP) && defined(CONFIG_ACPI)
 static int is_itpm(struct pnp_dev *dev)
 {
        struct acpi_device *acpi = pnp_acpi_device(dev);
@@ -93,6 +93,11 @@ static int is_itpm(struct pnp_dev *dev)
 
        return 0;
 }
+#else
+static inline int is_itpm(struct pnp_dev *dev)
+{
+       return 0;
+}
 #endif
 
 static int check_locality(struct tpm_chip *chip, int l)
index 3ee1fdb31ea7daac5a123d8e72252f86ab271426..e55814bc0d06f069dd5974e63c6656764975251c 100644 (file)
@@ -57,6 +57,7 @@ void proc_fork_connector(struct task_struct *task)
        struct proc_event *ev;
        __u8 buffer[CN_PROC_MSG_SIZE];
        struct timespec ts;
+       struct task_struct *parent;
 
        if (atomic_read(&proc_event_num_listeners) < 1)
                return;
@@ -67,8 +68,11 @@ void proc_fork_connector(struct task_struct *task)
        ktime_get_ts(&ts); /* get high res monotonic timestamp */
        put_unaligned(timespec_to_ns(&ts), (__u64 *)&ev->timestamp_ns);
        ev->what = PROC_EVENT_FORK;
-       ev->event_data.fork.parent_pid = task->real_parent->pid;
-       ev->event_data.fork.parent_tgid = task->real_parent->tgid;
+       rcu_read_lock();
+       parent = rcu_dereference(task->real_parent);
+       ev->event_data.fork.parent_pid = parent->pid;
+       ev->event_data.fork.parent_tgid = parent->tgid;
+       rcu_read_unlock();
        ev->event_data.fork.child_pid = task->pid;
        ev->event_data.fork.child_tgid = task->tgid;
 
index bf5092455a8f813f58fe88b07baaeea1b772ae87..d4c542372886b9cc92ae732590d2404196fb6eab 100644 (file)
@@ -25,9 +25,19 @@ DEFINE_PER_CPU(struct cpuidle_device *, cpuidle_devices);
 
 DEFINE_MUTEX(cpuidle_lock);
 LIST_HEAD(cpuidle_detected_devices);
-static void (*pm_idle_old)(void);
 
 static int enabled_devices;
+static int off __read_mostly;
+static int initialized __read_mostly;
+
+int cpuidle_disabled(void)
+{
+       return off;
+}
+void disable_cpuidle(void)
+{
+       off = 1;
+}
 
 #if defined(CONFIG_ARCH_HAS_CPU_IDLE_WAIT)
 static void cpuidle_kick_cpus(void)
@@ -46,25 +56,23 @@ static int __cpuidle_register_device(struct cpuidle_device *dev);
  * cpuidle_idle_call - the main idle loop
  *
  * NOTE: no locks or semaphores should be used here
+ * return non-zero on failure
  */
-static void cpuidle_idle_call(void)
+int cpuidle_idle_call(void)
 {
        struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
        struct cpuidle_state *target_state;
        int next_state;
 
+       if (off)
+               return -ENODEV;
+
+       if (!initialized)
+               return -ENODEV;
+
        /* check if the device is ready */
-       if (!dev || !dev->enabled) {
-               if (pm_idle_old)
-                       pm_idle_old();
-               else
-#if defined(CONFIG_ARCH_HAS_DEFAULT_IDLE)
-                       default_idle();
-#else
-                       local_irq_enable();
-#endif
-               return;
-       }
+       if (!dev || !dev->enabled)
+               return -EBUSY;
 
 #if 0
        /* shows regressions, re-enable for 2.6.29 */
@@ -89,7 +97,7 @@ static void cpuidle_idle_call(void)
        next_state = cpuidle_curr_governor->select(dev);
        if (need_resched()) {
                local_irq_enable();
-               return;
+               return 0;
        }
 
        target_state = &dev->states[next_state];
@@ -114,6 +122,8 @@ static void cpuidle_idle_call(void)
        /* give the governor an opportunity to reflect on the outcome */
        if (cpuidle_curr_governor->reflect)
                cpuidle_curr_governor->reflect(dev);
+
+       return 0;
 }
 
 /**
@@ -121,10 +131,10 @@ static void cpuidle_idle_call(void)
  */
 void cpuidle_install_idle_handler(void)
 {
-       if (enabled_devices && (pm_idle != cpuidle_idle_call)) {
+       if (enabled_devices) {
                /* Make sure all changes finished before we switch to new idle */
                smp_wmb();
-               pm_idle = cpuidle_idle_call;
+               initialized = 1;
        }
 }
 
@@ -133,8 +143,8 @@ void cpuidle_install_idle_handler(void)
  */
 void cpuidle_uninstall_idle_handler(void)
 {
-       if (enabled_devices && pm_idle_old && (pm_idle != pm_idle_old)) {
-               pm_idle = pm_idle_old;
+       if (enabled_devices) {
+               initialized = 0;
                cpuidle_kick_cpus();
        }
 }
@@ -427,7 +437,8 @@ static int __init cpuidle_init(void)
 {
        int ret;
 
-       pm_idle_old = pm_idle;
+       if (cpuidle_disabled())
+               return -ENODEV;
 
        ret = cpuidle_add_class_sysfs(&cpu_sysdev_class);
        if (ret)
@@ -438,4 +449,5 @@ static int __init cpuidle_init(void)
        return 0;
 }
 
+module_param(off, int, 0444);
 core_initcall(cpuidle_init);
index 33e50d556f178232f2cef79ae45e28f6923b325f..38c3fd8b9d763b1e5da516c85d10e36bf731f476 100644 (file)
@@ -13,6 +13,7 @@ extern struct list_head cpuidle_governors;
 extern struct list_head cpuidle_detected_devices;
 extern struct mutex cpuidle_lock;
 extern spinlock_t cpuidle_driver_lock;
+extern int cpuidle_disabled(void);
 
 /* idle loop */
 extern void cpuidle_install_idle_handler(void);
index fd1601e3d1250faaa3b4863eb27706529076bcb1..3f7e3cedd133b4ef66965351efb34351374b4af8 100644 (file)
@@ -26,6 +26,9 @@ int cpuidle_register_driver(struct cpuidle_driver *drv)
        if (!drv)
                return -EINVAL;
 
+       if (cpuidle_disabled())
+               return -ENODEV;
+
        spin_lock(&cpuidle_driver_lock);
        if (cpuidle_curr_driver) {
                spin_unlock(&cpuidle_driver_lock);
index 724c164d31c9a47ece237894888244cb4ec0e4b1..ea2f8e7aa24af2936e4b0766f70e36eac2941eea 100644 (file)
@@ -81,6 +81,9 @@ int cpuidle_register_governor(struct cpuidle_governor *gov)
        if (!gov || !gov->select)
                return -EINVAL;
 
+       if (cpuidle_disabled())
+               return -ENODEV;
+
        mutex_lock(&cpuidle_lock);
        if (__cpuidle_find_governor(gov->name) == NULL) {
                ret = 0;
index a4af8589330cceddfc613ef381d45b2a3a040540..734ed0206cd5ead8f9cc554e94ff168b59fc3dae 100644 (file)
@@ -9,6 +9,5 @@ TODO for slave dma
        - mxs-dma.c
        - dw_dmac
        - intel_mid_dma
-       - ste_dma40
 4. Check other subsystems for dma drivers and merge/move to dmaengine
 5. Remove dma_slave_config's dma direction.
index e6d7228b1479a6d462b1e19c323541fe5a767a7f..196a7378d33238ec0a08c1ac3672d428fff370cd 100644 (file)
@@ -156,14 +156,10 @@ struct pl08x_driver_data {
 #define PL08X_BOUNDARY_SHIFT           (10)    /* 1KB 0x400 */
 #define PL08X_BOUNDARY_SIZE            (1 << PL08X_BOUNDARY_SHIFT)
 
-/* Minimum period between work queue runs */
-#define PL08X_WQ_PERIODMIN     20
-
 /* Size (bytes) of each LLI buffer allocated for one transfer */
 # define PL08X_LLI_TSFR_SIZE   0x2000
 
 /* Maximum times we call dma_pool_alloc on this pool without freeing */
-#define PL08X_MAX_ALLOCS       0x40
 #define MAX_NUM_TSFR_LLIS      (PL08X_LLI_TSFR_SIZE/sizeof(struct pl08x_lli))
 #define PL08X_ALIGN            8
 
@@ -495,10 +491,10 @@ static inline u32 pl08x_cctl_bits(u32 cctl, u8 srcwidth, u8 dstwidth,
 
 struct pl08x_lli_build_data {
        struct pl08x_txd *txd;
-       struct pl08x_driver_data *pl08x;
        struct pl08x_bus_data srcbus;
        struct pl08x_bus_data dstbus;
        size_t remainder;
+       u32 lli_bus;
 };
 
 /*
@@ -551,8 +547,7 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd,
        llis_va[num_llis].src = bd->srcbus.addr;
        llis_va[num_llis].dst = bd->dstbus.addr;
        llis_va[num_llis].lli = llis_bus + (num_llis + 1) * sizeof(struct pl08x_lli);
-       if (bd->pl08x->lli_buses & PL08X_AHB2)
-               llis_va[num_llis].lli |= PL080_LLI_LM_AHB2;
+       llis_va[num_llis].lli |= bd->lli_bus;
 
        if (cctl & PL080_CONTROL_SRC_INCR)
                bd->srcbus.addr += len;
@@ -605,9 +600,9 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
        cctl = txd->cctl;
 
        bd.txd = txd;
-       bd.pl08x = pl08x;
        bd.srcbus.addr = txd->src_addr;
        bd.dstbus.addr = txd->dst_addr;
+       bd.lli_bus = (pl08x->lli_buses & PL08X_AHB2) ? PL080_LLI_LM_AHB2 : 0;
 
        /* Find maximum width of the source bus */
        bd.srcbus.maxwidth =
@@ -622,25 +617,15 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
        /* Set up the bus widths to the maximum */
        bd.srcbus.buswidth = bd.srcbus.maxwidth;
        bd.dstbus.buswidth = bd.dstbus.maxwidth;
-       dev_vdbg(&pl08x->adev->dev,
-                "%s source bus is %d bytes wide, dest bus is %d bytes wide\n",
-                __func__, bd.srcbus.buswidth, bd.dstbus.buswidth);
-
 
        /*
         * Bytes transferred == tsize * MIN(buswidths), not max(buswidths)
         */
        max_bytes_per_lli = min(bd.srcbus.buswidth, bd.dstbus.buswidth) *
                PL080_CONTROL_TRANSFER_SIZE_MASK;
-       dev_vdbg(&pl08x->adev->dev,
-                "%s max bytes per lli = %zu\n",
-                __func__, max_bytes_per_lli);
 
        /* We need to count this down to zero */
        bd.remainder = txd->len;
-       dev_vdbg(&pl08x->adev->dev,
-                "%s remainder = %zu\n",
-                __func__, bd.remainder);
 
        /*
         * Choose bus to align to
@@ -649,6 +634,16 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
         */
        pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl);
 
+       dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu llimax=%zu\n",
+                bd.srcbus.addr, cctl & PL080_CONTROL_SRC_INCR ? "+" : "",
+                bd.srcbus.buswidth,
+                bd.dstbus.addr, cctl & PL080_CONTROL_DST_INCR ? "+" : "",
+                bd.dstbus.buswidth,
+                bd.remainder, max_bytes_per_lli);
+       dev_vdbg(&pl08x->adev->dev, "mbus=%s sbus=%s\n",
+                mbus == &bd.srcbus ? "src" : "dst",
+                sbus == &bd.srcbus ? "src" : "dst");
+
        if (txd->len < mbus->buswidth) {
                /* Less than a bus width available - send as single bytes */
                while (bd.remainder) {
@@ -840,15 +835,14 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
        {
                int i;
 
+               dev_vdbg(&pl08x->adev->dev,
+                        "%-3s %-9s  %-10s %-10s %-10s %s\n",
+                        "lli", "", "csrc", "cdst", "clli", "cctl");
                for (i = 0; i < num_llis; i++) {
                        dev_vdbg(&pl08x->adev->dev,
-                                "lli %d @%p: csrc=0x%08x, cdst=0x%08x, cctl=0x%08x, clli=0x%08x\n",
-                                i,
-                                &llis_va[i],
-                                llis_va[i].src,
-                                llis_va[i].dst,
-                                llis_va[i].cctl,
-                                llis_va[i].lli
+                                "%3d @%p: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                                i, &llis_va[i], llis_va[i].src,
+                                llis_va[i].dst, llis_va[i].lli, llis_va[i].cctl
                                );
                }
        }
@@ -1054,64 +1048,105 @@ pl08x_dma_tx_status(struct dma_chan *chan,
 
 /* PrimeCell DMA extension */
 struct burst_table {
-       int burstwords;
+       u32 burstwords;
        u32 reg;
 };
 
 static const struct burst_table burst_sizes[] = {
        {
                .burstwords = 256,
-               .reg = (PL080_BSIZE_256 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_256 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_256,
        },
        {
                .burstwords = 128,
-               .reg = (PL080_BSIZE_128 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_128 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_128,
        },
        {
                .burstwords = 64,
-               .reg = (PL080_BSIZE_64 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_64 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_64,
        },
        {
                .burstwords = 32,
-               .reg = (PL080_BSIZE_32 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_32 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_32,
        },
        {
                .burstwords = 16,
-               .reg = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_16,
        },
        {
                .burstwords = 8,
-               .reg = (PL080_BSIZE_8 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_8 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_8,
        },
        {
                .burstwords = 4,
-               .reg = (PL080_BSIZE_4 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_4 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_4,
        },
        {
-               .burstwords = 1,
-               .reg = (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .burstwords = 0,
+               .reg = PL080_BSIZE_1,
        },
 };
 
+/*
+ * Given the source and destination available bus masks, select which
+ * will be routed to each port.  We try to have source and destination
+ * on separate ports, but always respect the allowable settings.
+ */
+static u32 pl08x_select_bus(u8 src, u8 dst)
+{
+       u32 cctl = 0;
+
+       if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1)))
+               cctl |= PL080_CONTROL_DST_AHB2;
+       if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2)))
+               cctl |= PL080_CONTROL_SRC_AHB2;
+
+       return cctl;
+}
+
+static u32 pl08x_cctl(u32 cctl)
+{
+       cctl &= ~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 |
+                 PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR |
+                 PL080_CONTROL_PROT_MASK);
+
+       /* Access the cell in privileged mode, non-bufferable, non-cacheable */
+       return cctl | PL080_CONTROL_PROT_SYS;
+}
+
+static u32 pl08x_width(enum dma_slave_buswidth width)
+{
+       switch (width) {
+       case DMA_SLAVE_BUSWIDTH_1_BYTE:
+               return PL080_WIDTH_8BIT;
+       case DMA_SLAVE_BUSWIDTH_2_BYTES:
+               return PL080_WIDTH_16BIT;
+       case DMA_SLAVE_BUSWIDTH_4_BYTES:
+               return PL080_WIDTH_32BIT;
+       default:
+               return ~0;
+       }
+}
+
+static u32 pl08x_burst(u32 maxburst)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(burst_sizes); i++)
+               if (burst_sizes[i].burstwords <= maxburst)
+                       break;
+
+       return burst_sizes[i].reg;
+}
+
 static int dma_set_runtime_config(struct dma_chan *chan,
                                  struct dma_slave_config *config)
 {
        struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
        struct pl08x_driver_data *pl08x = plchan->host;
-       struct pl08x_channel_data *cd = plchan->cd;
        enum dma_slave_buswidth addr_width;
-       dma_addr_t addr;
-       u32 maxburst;
+       u32 width, burst, maxburst;
        u32 cctl = 0;
-       int i;
 
        if (!plchan->slave)
                return -EINVAL;
@@ -1119,11 +1154,9 @@ static int dma_set_runtime_config(struct dma_chan *chan,
        /* Transfer direction */
        plchan->runtime_direction = config->direction;
        if (config->direction == DMA_TO_DEVICE) {
-               addr = config->dst_addr;
                addr_width = config->dst_addr_width;
                maxburst = config->dst_maxburst;
        } else if (config->direction == DMA_FROM_DEVICE) {
-               addr = config->src_addr;
                addr_width = config->src_addr_width;
                maxburst = config->src_maxburst;
        } else {
@@ -1132,46 +1165,40 @@ static int dma_set_runtime_config(struct dma_chan *chan,
                return -EINVAL;
        }
 
-       switch (addr_width) {
-       case DMA_SLAVE_BUSWIDTH_1_BYTE:
-               cctl |= (PL080_WIDTH_8BIT << PL080_CONTROL_SWIDTH_SHIFT) |
-                       (PL080_WIDTH_8BIT << PL080_CONTROL_DWIDTH_SHIFT);
-               break;
-       case DMA_SLAVE_BUSWIDTH_2_BYTES:
-               cctl |= (PL080_WIDTH_16BIT << PL080_CONTROL_SWIDTH_SHIFT) |
-                       (PL080_WIDTH_16BIT << PL080_CONTROL_DWIDTH_SHIFT);
-               break;
-       case DMA_SLAVE_BUSWIDTH_4_BYTES:
-               cctl |= (PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT) |
-                       (PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT);
-               break;
-       default:
+       width = pl08x_width(addr_width);
+       if (width == ~0) {
                dev_err(&pl08x->adev->dev,
                        "bad runtime_config: alien address width\n");
                return -EINVAL;
        }
 
+       cctl |= width << PL080_CONTROL_SWIDTH_SHIFT;
+       cctl |= width << PL080_CONTROL_DWIDTH_SHIFT;
+
        /*
-        * Now decide on a maxburst:
         * If this channel will only request single transfers, set this
         * down to ONE element.  Also select one element if no maxburst
         * is specified.
         */
-       if (plchan->cd->single || maxburst == 0) {
-               cctl |= (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT);
+       if (plchan->cd->single)
+               maxburst = 1;
+
+       burst = pl08x_burst(maxburst);
+       cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT;
+       cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT;
+
+       if (plchan->runtime_direction == DMA_FROM_DEVICE) {
+               plchan->src_addr = config->src_addr;
+               plchan->src_cctl = pl08x_cctl(cctl) | PL080_CONTROL_DST_INCR |
+                       pl08x_select_bus(plchan->cd->periph_buses,
+                                        pl08x->mem_buses);
        } else {
-               for (i = 0; i < ARRAY_SIZE(burst_sizes); i++)
-                       if (burst_sizes[i].burstwords <= maxburst)
-                               break;
-               cctl |= burst_sizes[i].reg;
+               plchan->dst_addr = config->dst_addr;
+               plchan->dst_cctl = pl08x_cctl(cctl) | PL080_CONTROL_SRC_INCR |
+                       pl08x_select_bus(pl08x->mem_buses,
+                                        plchan->cd->periph_buses);
        }
 
-       plchan->runtime_addr = addr;
-
-       /* Modify the default channel data to fit PrimeCell request */
-       cd->cctl = cctl;
-
        dev_dbg(&pl08x->adev->dev,
                "configured channel %s (%s) for %s, data width %d, "
                "maxburst %d words, LE, CCTL=0x%08x\n",
@@ -1270,23 +1297,6 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
        return 0;
 }
 
-/*
- * Given the source and destination available bus masks, select which
- * will be routed to each port.  We try to have source and destination
- * on separate ports, but always respect the allowable settings.
- */
-static u32 pl08x_select_bus(struct pl08x_driver_data *pl08x, u8 src, u8 dst)
-{
-       u32 cctl = 0;
-
-       if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1)))
-               cctl |= PL080_CONTROL_DST_AHB2;
-       if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2)))
-               cctl |= PL080_CONTROL_SRC_AHB2;
-
-       return cctl;
-}
-
 static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan,
        unsigned long flags)
 {
@@ -1338,8 +1348,8 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
        txd->cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR;
 
        if (pl08x->vd->dualmaster)
-               txd->cctl |= pl08x_select_bus(pl08x,
-                                       pl08x->mem_buses, pl08x->mem_buses);
+               txd->cctl |= pl08x_select_bus(pl08x->mem_buses,
+                                             pl08x->mem_buses);
 
        ret = pl08x_prep_channel_resources(plchan, txd);
        if (ret)
@@ -1356,7 +1366,6 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
        struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
        struct pl08x_driver_data *pl08x = plchan->host;
        struct pl08x_txd *txd;
-       u8 src_buses, dst_buses;
        int ret;
 
        /*
@@ -1390,42 +1399,22 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
        txd->direction = direction;
        txd->len = sgl->length;
 
-       txd->cctl = plchan->cd->cctl &
-                       ~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 |
-                         PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR |
-                         PL080_CONTROL_PROT_MASK);
-
-       /* Access the cell in privileged mode, non-bufferable, non-cacheable */
-       txd->cctl |= PL080_CONTROL_PROT_SYS;
-
        if (direction == DMA_TO_DEVICE) {
                txd->ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT;
-               txd->cctl |= PL080_CONTROL_SRC_INCR;
+               txd->cctl = plchan->dst_cctl;
                txd->src_addr = sgl->dma_address;
-               if (plchan->runtime_addr)
-                       txd->dst_addr = plchan->runtime_addr;
-               else
-                       txd->dst_addr = plchan->cd->addr;
-               src_buses = pl08x->mem_buses;
-               dst_buses = plchan->cd->periph_buses;
+               txd->dst_addr = plchan->dst_addr;
        } else if (direction == DMA_FROM_DEVICE) {
                txd->ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
-               txd->cctl |= PL080_CONTROL_DST_INCR;
-               if (plchan->runtime_addr)
-                       txd->src_addr = plchan->runtime_addr;
-               else
-                       txd->src_addr = plchan->cd->addr;
+               txd->cctl = plchan->src_cctl;
+               txd->src_addr = plchan->src_addr;
                txd->dst_addr = sgl->dma_address;
-               src_buses = plchan->cd->periph_buses;
-               dst_buses = pl08x->mem_buses;
        } else {
                dev_err(&pl08x->adev->dev,
                        "%s direction unsupported\n", __func__);
                return NULL;
        }
 
-       txd->cctl |= pl08x_select_bus(pl08x, src_buses, dst_buses);
-
        ret = pl08x_prep_channel_resources(plchan, txd);
        if (ret)
                return NULL;
@@ -1676,6 +1665,20 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
        return mask ? IRQ_HANDLED : IRQ_NONE;
 }
 
+static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan)
+{
+       u32 cctl = pl08x_cctl(chan->cd->cctl);
+
+       chan->slave = true;
+       chan->name = chan->cd->bus_id;
+       chan->src_addr = chan->cd->addr;
+       chan->dst_addr = chan->cd->addr;
+       chan->src_cctl = cctl | PL080_CONTROL_DST_INCR |
+               pl08x_select_bus(chan->cd->periph_buses, chan->host->mem_buses);
+       chan->dst_cctl = cctl | PL080_CONTROL_SRC_INCR |
+               pl08x_select_bus(chan->host->mem_buses, chan->cd->periph_buses);
+}
+
 /*
  * Initialise the DMAC memcpy/slave channels.
  * Make a local wrapper to hold required data
@@ -1707,9 +1710,8 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
                chan->state = PL08X_CHAN_IDLE;
 
                if (slave) {
-                       chan->slave = true;
-                       chan->name = pl08x->pd->slave_channels[i].bus_id;
                        chan->cd = &pl08x->pd->slave_channels[i];
+                       pl08x_dma_slave_init(chan);
                } else {
                        chan->cd = &pl08x->pd->memcpy_channel;
                        chan->name = kasprintf(GFP_KERNEL, "memcpy%d", i);
index 36144f88d718f383b232b25d168030077f6769d3..6a483eac7b3f2d971cabae0aceaf022a42866558 100644 (file)
@@ -1216,7 +1216,7 @@ static int __init at_dma_probe(struct platform_device *pdev)
        atdma->dma_common.cap_mask = pdata->cap_mask;
        atdma->all_chan_mask = (1 << pdata->nr_channels) - 1;
 
-       size = io->end - io->start + 1;
+       size = resource_size(io);
        if (!request_mem_region(io->start, size, pdev->dev.driver->name)) {
                err = -EBUSY;
                goto err_kfree;
@@ -1362,7 +1362,7 @@ static int __exit at_dma_remove(struct platform_device *pdev)
        atdma->regs = NULL;
 
        io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(io->start, io->end - io->start + 1);
+       release_mem_region(io->start, resource_size(io));
 
        kfree(atdma);
 
index a92d95eac86b204fcb62dd1dafbe8a7150957807..4234f416ef115055cb425822a2c411a73dd41590 100644 (file)
@@ -41,6 +41,8 @@ struct coh901318_desc {
        struct coh901318_lli *lli;
        enum dma_data_direction dir;
        unsigned long flags;
+       u32 head_config;
+       u32 head_ctrl;
 };
 
 struct coh901318_base {
@@ -661,6 +663,9 @@ static struct coh901318_desc *coh901318_queue_start(struct coh901318_chan *cohc)
 
                coh901318_desc_submit(cohc, cohd);
 
+               /* Program the transaction head */
+               coh901318_set_conf(cohc, cohd->head_config);
+               coh901318_set_ctrl(cohc, cohd->head_ctrl);
                coh901318_prep_linked_list(cohc, cohd->lli);
 
                /* start dma job on this channel */
@@ -1091,8 +1096,6 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        } else
                goto err_direction;
 
-       coh901318_set_conf(cohc, config);
-
        /* The dma only supports transmitting packages up to
         * MAX_DMA_PACKET_SIZE. Calculate to total number of
         * dma elemts required to send the entire sg list
@@ -1129,16 +1132,18 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        if (ret)
                goto err_lli_fill;
 
-       /*
-        * Set the default ctrl for the channel to the one from the lli,
-        * things may have changed due to odd buffer alignment etc.
-        */
-       coh901318_set_ctrl(cohc, lli->control);
 
        COH_DBG(coh901318_list_print(cohc, lli));
 
        /* Pick a descriptor to handle this transfer */
        cohd = coh901318_desc_get(cohc);
+       cohd->head_config = config;
+       /*
+        * Set the default head ctrl for the channel to the one from the
+        * lli, things may have changed due to odd buffer alignment
+        * etc.
+        */
+       cohd->head_ctrl = lli->control;
        cohd->dir = direction;
        cohd->flags = flags;
        cohd->desc.tx_submit = coh901318_tx_submit;
index 48694c34d96bd95fe90ebf5eb377877c3851817a..b48967b499da0bad30529501492def4695dc1f25 100644 (file)
@@ -62,9 +62,9 @@
 #include <linux/slab.h>
 
 static DEFINE_MUTEX(dma_list_mutex);
+static DEFINE_IDR(dma_idr);
 static LIST_HEAD(dma_device_list);
 static long dmaengine_ref_count;
-static struct idr dma_idr;
 
 /* --- sysfs implementation --- */
 
@@ -510,8 +510,8 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v
                                         dma_chan_name(chan));
                                list_del_rcu(&device->global_node);
                        } else if (err)
-                               pr_err("dmaengine: failed to get %s: (%d)\n",
-                                      dma_chan_name(chan), err);
+                               pr_debug("dmaengine: failed to get %s: (%d)\n",
+                                        dma_chan_name(chan), err);
                        else
                                break;
                        if (--device->privatecnt == 0)
@@ -1050,8 +1050,6 @@ EXPORT_SYMBOL_GPL(dma_run_dependencies);
 
 static int __init dma_bus_init(void)
 {
-       idr_init(&dma_idr);
-       mutex_init(&dma_list_mutex);
        return class_register(&dma_devclass);
 }
 arch_initcall(dma_bus_init);
index 0766c1e53b1d760bf08dd01a919abbf3ab9f0eda..5d7a49bd7c2658ec53887781d2063d2046413b35 100644 (file)
@@ -902,7 +902,7 @@ static void ep93xx_dma_free_chan_resources(struct dma_chan *chan)
  *
  * Returns a valid DMA descriptor or %NULL in case of failure.
  */
-struct dma_async_tx_descriptor *
+static struct dma_async_tx_descriptor *
 ep93xx_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
                           dma_addr_t src, size_t len, unsigned long flags)
 {
index 1eb60ded2f0de21b29bdd672b3609f8a3b7d716b..7bd7e98548cd34e495910f093ee44e821c1de67b 100644 (file)
@@ -1305,8 +1305,10 @@ static int __init sdma_probe(struct platform_device *pdev)
                goto err_request_irq;
 
        sdma->script_addrs = kzalloc(sizeof(*sdma->script_addrs), GFP_KERNEL);
-       if (!sdma->script_addrs)
+       if (!sdma->script_addrs) {
+               ret = -ENOMEM;
                goto err_alloc;
+       }
 
        if (of_id)
                pdev->id_entry = of_id->data;
index f653517ef7445c484580ef140b64315781c05d13..8a3fdd87db97a462ed4738f1c294276ca0817367 100644 (file)
@@ -1351,7 +1351,6 @@ int dma_suspend(struct pci_dev *pci, pm_message_t state)
                        return -EAGAIN;
        }
        device->state = SUSPENDED;
-       pci_set_drvdata(pci, device);
        pci_save_state(pci);
        pci_disable_device(pci);
        pci_set_power_state(pci, PCI_D3hot);
@@ -1380,7 +1379,6 @@ int dma_resume(struct pci_dev *pci)
        }
        device->state = RUNNING;
        iowrite32(REG_BIT0, device->dma_base + DMA_CFG);
-       pci_set_drvdata(pci, device);
        return 0;
 }
 
index d845dc4b71039a88787cf6cd1836c189772d3e23..f519c93a61e78c4778afeebe7984c4b45a44acc3 100644 (file)
 /* provide a lookup table for setting the source address in the base or
  * extended descriptor of an xor or pq descriptor
  */
-static const u8 xor_idx_to_desc __read_mostly = 0xd0;
-static const u8 xor_idx_to_field[] __read_mostly = { 1, 4, 5, 6, 7, 0, 1, 2 };
-static const u8 pq_idx_to_desc __read_mostly = 0xf8;
-static const u8 pq_idx_to_field[] __read_mostly = { 1, 4, 5, 0, 1, 2, 4, 5 };
+static const u8 xor_idx_to_desc = 0xe0;
+static const u8 xor_idx_to_field[] = { 1, 4, 5, 6, 7, 0, 1, 2 };
+static const u8 pq_idx_to_desc = 0xf8;
+static const u8 pq_idx_to_field[] = { 1, 4, 5, 0, 1, 2, 4, 5 };
 
 static dma_addr_t xor_get_src(struct ioat_raw_descriptor *descs[2], int idx)
 {
index fab37d1cf48d0bdf9f4476b1758cdc3752cd72f4..5e3a40f7994510d6b084815459e77f4690da8970 100644 (file)
@@ -72,6 +72,17 @@ static struct pci_device_id ioat_pci_tbl[] = {
        { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF8) },
        { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_JSF9) },
 
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB0) },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB1) },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB2) },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB3) },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB4) },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB5) },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB6) },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB7) },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB8) },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB9) },
+
        { 0, }
 };
 MODULE_DEVICE_TABLE(pci, ioat_pci_tbl);
index fd7d2b308cf2119f53c83107f45d5283e0620322..6815905a772f786b41095d37bc115deec8b9ca2d 100644 (file)
@@ -1706,16 +1706,14 @@ static int __init ipu_probe(struct platform_device *pdev)
                ipu_data.irq_fn, ipu_data.irq_err, ipu_data.irq_base);
 
        /* Remap IPU common registers */
-       ipu_data.reg_ipu = ioremap(mem_ipu->start,
-                                  mem_ipu->end - mem_ipu->start + 1);
+       ipu_data.reg_ipu = ioremap(mem_ipu->start, resource_size(mem_ipu));
        if (!ipu_data.reg_ipu) {
                ret = -ENOMEM;
                goto err_ioremap_ipu;
        }
 
        /* Remap Image Converter and Image DMA Controller registers */
-       ipu_data.reg_ic = ioremap(mem_ic->start,
-                                 mem_ic->end - mem_ic->start + 1);
+       ipu_data.reg_ic = ioremap(mem_ic->start, resource_size(mem_ic));
        if (!ipu_data.reg_ic) {
                ret = -ENOMEM;
                goto err_ioremap_ic;
index 06f9f27dbe7cdfcd1639f3644727d23ed5a20799..9a353c2216d09a1c1a498da4344f31a03f90aad3 100644 (file)
@@ -1304,7 +1304,8 @@ static int mv_xor_shared_probe(struct platform_device *pdev)
        if (!res)
                return -ENODEV;
 
-       msp->xor_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+       msp->xor_base = devm_ioremap(&pdev->dev, res->start,
+                                    resource_size(res));
        if (!msp->xor_base)
                return -EBUSY;
 
index 88aad4f540026cbe3b653f4e9d40391cf342aaf0..be641cbd36fcb30c16aafaf25f279ecc0064b07a 100644 (file)
@@ -327,10 +327,12 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan)
 
        memset(mxs_chan->ccw, 0, PAGE_SIZE);
 
-       ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler,
-                               0, "mxs-dma", mxs_dma);
-       if (ret)
-               goto err_irq;
+       if (mxs_chan->chan_irq != NO_IRQ) {
+               ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler,
+                                       0, "mxs-dma", mxs_dma);
+               if (ret)
+                       goto err_irq;
+       }
 
        ret = clk_enable(mxs_dma->clk);
        if (ret)
@@ -535,6 +537,7 @@ static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        switch (cmd) {
        case DMA_TERMINATE_ALL:
                mxs_dma_disable_chan(mxs_chan);
+               mxs_dma_reset_chan(mxs_chan);
                break;
        case DMA_PAUSE:
                mxs_dma_pause_chan(mxs_chan);
@@ -707,6 +710,8 @@ static struct platform_device_id mxs_dma_type[] = {
        }, {
                .name = "mxs-dma-apbx",
                .driver_data = MXS_DMA_APBX,
+       }, {
+               /* end of list */
        }
 };
 
index ff5b38f9d45bc98249ddf2a648a5b338f63f3496..1ac8d4b580b732e1cad7a25cfc32a7a068f67c92 100644 (file)
@@ -45,7 +45,8 @@
 #define DMA_STATUS_MASK_BITS           0x3
 #define DMA_STATUS_SHIFT_BITS          16
 #define DMA_STATUS_IRQ(x)              (0x1 << (x))
-#define DMA_STATUS_ERR(x)              (0x1 << ((x) + 8))
+#define DMA_STATUS0_ERR(x)             (0x1 << ((x) + 8))
+#define DMA_STATUS2_ERR(x)             (0x1 << (x))
 
 #define DMA_DESC_WIDTH_SHIFT_BITS      12
 #define DMA_DESC_WIDTH_1_BYTE          (0x3 << DMA_DESC_WIDTH_SHIFT_BITS)
@@ -61,6 +62,9 @@
 
 #define MAX_CHAN_NR                    8
 
+#define DMA_MASK_CTL0_MODE     0x33333333
+#define DMA_MASK_CTL2_MODE     0x00003333
+
 static unsigned int init_nr_desc_per_channel = 64;
 module_param(init_nr_desc_per_channel, uint, 0644);
 MODULE_PARM_DESC(init_nr_desc_per_channel,
@@ -133,6 +137,7 @@ struct pch_dma {
 #define PCH_DMA_CTL3   0x0C
 #define PCH_DMA_STS0   0x10
 #define PCH_DMA_STS1   0x14
+#define PCH_DMA_STS2   0x18
 
 #define dma_readl(pd, name) \
        readl((pd)->membase + PCH_DMA_##name)
@@ -183,13 +188,19 @@ static void pdc_enable_irq(struct dma_chan *chan, int enable)
 {
        struct pch_dma *pd = to_pd(chan->device);
        u32 val;
+       int pos;
+
+       if (chan->chan_id < 8)
+               pos = chan->chan_id;
+       else
+               pos = chan->chan_id + 8;
 
        val = dma_readl(pd, CTL2);
 
        if (enable)
-               val |= 0x1 << chan->chan_id;
+               val |= 0x1 << pos;
        else
-               val &= ~(0x1 << chan->chan_id);
+               val &= ~(0x1 << pos);
 
        dma_writel(pd, CTL2, val);
 
@@ -202,10 +213,17 @@ static void pdc_set_dir(struct dma_chan *chan)
        struct pch_dma_chan *pd_chan = to_pd_chan(chan);
        struct pch_dma *pd = to_pd(chan->device);
        u32 val;
+       u32 mask_mode;
+       u32 mask_ctl;
 
        if (chan->chan_id < 8) {
                val = dma_readl(pd, CTL0);
 
+               mask_mode = DMA_CTL0_MODE_MASK_BITS <<
+                                       (DMA_CTL0_BITS_PER_CH * chan->chan_id);
+               mask_ctl = DMA_MASK_CTL0_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+                                      (DMA_CTL0_BITS_PER_CH * chan->chan_id));
+               val &= mask_mode;
                if (pd_chan->dir == DMA_TO_DEVICE)
                        val |= 0x1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +
                                       DMA_CTL0_DIR_SHIFT_BITS);
@@ -213,18 +231,24 @@ static void pdc_set_dir(struct dma_chan *chan)
                        val &= ~(0x1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +
                                         DMA_CTL0_DIR_SHIFT_BITS));
 
+               val |= mask_ctl;
                dma_writel(pd, CTL0, val);
        } else {
                int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */
                val = dma_readl(pd, CTL3);
 
+               mask_mode = DMA_CTL0_MODE_MASK_BITS <<
+                                               (DMA_CTL0_BITS_PER_CH * ch);
+               mask_ctl = DMA_MASK_CTL2_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+                                                (DMA_CTL0_BITS_PER_CH * ch));
+               val &= mask_mode;
                if (pd_chan->dir == DMA_TO_DEVICE)
                        val |= 0x1 << (DMA_CTL0_BITS_PER_CH * ch +
                                       DMA_CTL0_DIR_SHIFT_BITS);
                else
                        val &= ~(0x1 << (DMA_CTL0_BITS_PER_CH * ch +
                                         DMA_CTL0_DIR_SHIFT_BITS));
-
+               val |= mask_ctl;
                dma_writel(pd, CTL3, val);
        }
 
@@ -236,33 +260,37 @@ static void pdc_set_mode(struct dma_chan *chan, u32 mode)
 {
        struct pch_dma *pd = to_pd(chan->device);
        u32 val;
+       u32 mask_ctl;
+       u32 mask_dir;
 
        if (chan->chan_id < 8) {
+               mask_ctl = DMA_MASK_CTL0_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+                          (DMA_CTL0_BITS_PER_CH * chan->chan_id));
+               mask_dir = 1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +\
+                                DMA_CTL0_DIR_SHIFT_BITS);
                val = dma_readl(pd, CTL0);
-
-               val &= ~(DMA_CTL0_MODE_MASK_BITS <<
-                       (DMA_CTL0_BITS_PER_CH * chan->chan_id));
+               val &= mask_dir;
                val |= mode << (DMA_CTL0_BITS_PER_CH * chan->chan_id);
-
+               val |= mask_ctl;
                dma_writel(pd, CTL0, val);
        } else {
                int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */
-
+               mask_ctl = DMA_MASK_CTL2_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+                                                (DMA_CTL0_BITS_PER_CH * ch));
+               mask_dir = 1 << (DMA_CTL0_BITS_PER_CH * ch +\
+                                DMA_CTL0_DIR_SHIFT_BITS);
                val = dma_readl(pd, CTL3);
-
-               val &= ~(DMA_CTL0_MODE_MASK_BITS <<
-                       (DMA_CTL0_BITS_PER_CH * ch));
+               val &= mask_dir;
                val |= mode << (DMA_CTL0_BITS_PER_CH * ch);
-
+               val |= mask_ctl;
                dma_writel(pd, CTL3, val);
-
        }
 
        dev_dbg(chan2dev(chan), "pdc_set_mode: chan %d -> %x\n",
                chan->chan_id, val);
 }
 
-static u32 pdc_get_status(struct pch_dma_chan *pd_chan)
+static u32 pdc_get_status0(struct pch_dma_chan *pd_chan)
 {
        struct pch_dma *pd = to_pd(pd_chan->chan.device);
        u32 val;
@@ -272,9 +300,27 @@ static u32 pdc_get_status(struct pch_dma_chan *pd_chan)
                        DMA_STATUS_BITS_PER_CH * pd_chan->chan.chan_id));
 }
 
+static u32 pdc_get_status2(struct pch_dma_chan *pd_chan)
+{
+       struct pch_dma *pd = to_pd(pd_chan->chan.device);
+       u32 val;
+
+       val = dma_readl(pd, STS2);
+       return DMA_STATUS_MASK_BITS & (val >> (DMA_STATUS_SHIFT_BITS +
+                       DMA_STATUS_BITS_PER_CH * (pd_chan->chan.chan_id - 8)));
+}
+
 static bool pdc_is_idle(struct pch_dma_chan *pd_chan)
 {
-       if (pdc_get_status(pd_chan) == DMA_STATUS_IDLE)
+       u32 sts;
+
+       if (pd_chan->chan.chan_id < 8)
+               sts = pdc_get_status0(pd_chan);
+       else
+               sts = pdc_get_status2(pd_chan);
+
+
+       if (sts == DMA_STATUS_IDLE)
                return true;
        else
                return false;
@@ -495,11 +541,11 @@ static int pd_alloc_chan_resources(struct dma_chan *chan)
                list_add_tail(&desc->desc_node, &tmp_list);
        }
 
-       spin_lock_bh(&pd_chan->lock);
+       spin_lock_irq(&pd_chan->lock);
        list_splice(&tmp_list, &pd_chan->free_list);
        pd_chan->descs_allocated = i;
        pd_chan->completed_cookie = chan->cookie = 1;
-       spin_unlock_bh(&pd_chan->lock);
+       spin_unlock_irq(&pd_chan->lock);
 
        pdc_enable_irq(chan, 1);
 
@@ -517,10 +563,10 @@ static void pd_free_chan_resources(struct dma_chan *chan)
        BUG_ON(!list_empty(&pd_chan->active_list));
        BUG_ON(!list_empty(&pd_chan->queue));
 
-       spin_lock_bh(&pd_chan->lock);
+       spin_lock_irq(&pd_chan->lock);
        list_splice_init(&pd_chan->free_list, &tmp_list);
        pd_chan->descs_allocated = 0;
-       spin_unlock_bh(&pd_chan->lock);
+       spin_unlock_irq(&pd_chan->lock);
 
        list_for_each_entry_safe(desc, _d, &tmp_list, desc_node)
                pci_pool_free(pd->pool, desc, desc->txd.phys);
@@ -536,10 +582,10 @@ static enum dma_status pd_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
        dma_cookie_t last_completed;
        int ret;
 
-       spin_lock_bh(&pd_chan->lock);
+       spin_lock_irq(&pd_chan->lock);
        last_completed = pd_chan->completed_cookie;
        last_used = chan->cookie;
-       spin_unlock_bh(&pd_chan->lock);
+       spin_unlock_irq(&pd_chan->lock);
 
        ret = dma_async_is_complete(cookie, last_completed, last_used);
 
@@ -654,7 +700,7 @@ static int pd_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        if (cmd != DMA_TERMINATE_ALL)
                return -ENXIO;
 
-       spin_lock_bh(&pd_chan->lock);
+       spin_lock_irq(&pd_chan->lock);
 
        pdc_set_mode(&pd_chan->chan, DMA_CTL0_DISABLE);
 
@@ -664,7 +710,7 @@ static int pd_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        list_for_each_entry_safe(desc, _d, &list, desc_node)
                pdc_chain_complete(pd_chan, desc);
 
-       spin_unlock_bh(&pd_chan->lock);
+       spin_unlock_irq(&pd_chan->lock);
 
        return 0;
 }
@@ -693,30 +739,45 @@ static irqreturn_t pd_irq(int irq, void *devid)
        struct pch_dma *pd = (struct pch_dma *)devid;
        struct pch_dma_chan *pd_chan;
        u32 sts0;
+       u32 sts2;
        int i;
-       int ret = IRQ_NONE;
+       int ret0 = IRQ_NONE;
+       int ret2 = IRQ_NONE;
 
        sts0 = dma_readl(pd, STS0);
+       sts2 = dma_readl(pd, STS2);
 
        dev_dbg(pd->dma.dev, "pd_irq sts0: %x\n", sts0);
 
        for (i = 0; i < pd->dma.chancnt; i++) {
                pd_chan = &pd->channels[i];
 
-               if (sts0 & DMA_STATUS_IRQ(i)) {
-                       if (sts0 & DMA_STATUS_ERR(i))
-                               set_bit(0, &pd_chan->err_status);
+               if (i < 8) {
+                       if (sts0 & DMA_STATUS_IRQ(i)) {
+                               if (sts0 & DMA_STATUS0_ERR(i))
+                                       set_bit(0, &pd_chan->err_status);
 
-                       tasklet_schedule(&pd_chan->tasklet);
-                       ret = IRQ_HANDLED;
-               }
+                               tasklet_schedule(&pd_chan->tasklet);
+                               ret0 = IRQ_HANDLED;
+                       }
+               } else {
+                       if (sts2 & DMA_STATUS_IRQ(i - 8)) {
+                               if (sts2 & DMA_STATUS2_ERR(i))
+                                       set_bit(0, &pd_chan->err_status);
 
+                               tasklet_schedule(&pd_chan->tasklet);
+                               ret2 = IRQ_HANDLED;
+                       }
+               }
        }
 
        /* clear interrupt bits in status register */
-       dma_writel(pd, STS0, sts0);
+       if (ret0)
+               dma_writel(pd, STS0, sts0);
+       if (ret2)
+               dma_writel(pd, STS2, sts2);
 
-       return ret;
+       return ret0 | ret2;
 }
 
 #ifdef CONFIG_PM
index 6abe1ec1f2ce853300524f46ed5ebd0a0d226961..00eee59e8b3357929a3a42f4861921f8ca2b858b 100644 (file)
@@ -82,7 +82,7 @@ struct dma_pl330_dmac {
        spinlock_t pool_lock;
 
        /* Peripheral channels connected to this DMAC */
-       struct dma_pl330_chan peripherals[0]; /* keep at end */
+       struct dma_pl330_chan *peripherals; /* keep at end */
 };
 
 struct dma_pl330_desc {
@@ -451,8 +451,13 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch)
        desc->txd.cookie = 0;
        async_tx_ack(&desc->txd);
 
-       desc->req.rqtype = peri->rqtype;
-       desc->req.peri = peri->peri_id;
+       if (peri) {
+               desc->req.rqtype = peri->rqtype;
+               desc->req.peri = peri->peri_id;
+       } else {
+               desc->req.rqtype = MEMTOMEM;
+               desc->req.peri = 0;
+       }
 
        dma_async_tx_descriptor_init(&desc->txd, &pch->chan);
 
@@ -529,10 +534,10 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
        struct pl330_info *pi;
        int burst;
 
-       if (unlikely(!pch || !len || !peri))
+       if (unlikely(!pch || !len))
                return NULL;
 
-       if (peri->rqtype != MEMTOMEM)
+       if (peri && peri->rqtype != MEMTOMEM)
                return NULL;
 
        pi = &pch->dmac->pif;
@@ -577,7 +582,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        int i, burst_size;
        dma_addr_t addr;
 
-       if (unlikely(!pch || !sgl || !sg_len))
+       if (unlikely(!pch || !sgl || !sg_len || !peri))
                return NULL;
 
        /* Make sure the direction is consistent */
@@ -666,17 +671,12 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
        struct dma_device *pd;
        struct resource *res;
        int i, ret, irq;
+       int num_chan;
 
        pdat = adev->dev.platform_data;
 
-       if (!pdat || !pdat->nr_valid_peri) {
-               dev_err(&adev->dev, "platform data missing\n");
-               return -ENODEV;
-       }
-
        /* Allocate a new DMAC and its Channels */
-       pdmac = kzalloc(pdat->nr_valid_peri * sizeof(*pch)
-                               + sizeof(*pdmac), GFP_KERNEL);
+       pdmac = kzalloc(sizeof(*pdmac), GFP_KERNEL);
        if (!pdmac) {
                dev_err(&adev->dev, "unable to allocate mem\n");
                return -ENOMEM;
@@ -685,7 +685,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
        pi = &pdmac->pif;
        pi->dev = &adev->dev;
        pi->pl330_data = NULL;
-       pi->mcbufsz = pdat->mcbuf_sz;
+       pi->mcbufsz = pdat ? pdat->mcbuf_sz : 0;
 
        res = &adev->res;
        request_mem_region(res->start, resource_size(res), "dma-pl330");
@@ -717,27 +717,35 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
        INIT_LIST_HEAD(&pd->channels);
 
        /* Initialize channel parameters */
-       for (i = 0; i < pdat->nr_valid_peri; i++) {
-               struct dma_pl330_peri *peri = &pdat->peri[i];
-               pch = &pdmac->peripherals[i];
+       num_chan = max(pdat ? pdat->nr_valid_peri : 0, (u8)pi->pcfg.num_chan);
+       pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL);
 
-               switch (peri->rqtype) {
-               case MEMTOMEM:
+       for (i = 0; i < num_chan; i++) {
+               pch = &pdmac->peripherals[i];
+               if (pdat) {
+                       struct dma_pl330_peri *peri = &pdat->peri[i];
+
+                       switch (peri->rqtype) {
+                       case MEMTOMEM:
+                               dma_cap_set(DMA_MEMCPY, pd->cap_mask);
+                               break;
+                       case MEMTODEV:
+                       case DEVTOMEM:
+                               dma_cap_set(DMA_SLAVE, pd->cap_mask);
+                               break;
+                       default:
+                               dev_err(&adev->dev, "DEVTODEV Not Supported\n");
+                               continue;
+                       }
+                       pch->chan.private = peri;
+               } else {
                        dma_cap_set(DMA_MEMCPY, pd->cap_mask);
-                       break;
-               case MEMTODEV:
-               case DEVTOMEM:
-                       dma_cap_set(DMA_SLAVE, pd->cap_mask);
-                       break;
-               default:
-                       dev_err(&adev->dev, "DEVTODEV Not Supported\n");
-                       continue;
+                       pch->chan.private = NULL;
                }
 
                INIT_LIST_HEAD(&pch->work_list);
                spin_lock_init(&pch->lock);
                pch->pl330_chid = NULL;
-               pch->chan.private = peri;
                pch->chan.device = pd;
                pch->chan.chan_id = i;
                pch->dmac = pdmac;
index 29d1addbe0cf039ce9beb0bf7660022cbbda1e4e..cd3a7c726bf87bef330f882981afc2fc42be964e 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/err.h>
+#include <linux/amba/bus.h>
 
 #include <plat/ste_dma40.h>
 
@@ -45,9 +46,6 @@
 #define D40_ALLOC_PHY          (1 << 30)
 #define D40_ALLOC_LOG_FREE     0
 
-/* Hardware designer of the block */
-#define D40_HW_DESIGNER 0x8
-
 /**
  * enum 40_command - The different commands and/or statuses.
  *
@@ -186,6 +184,8 @@ struct d40_base;
  * @log_def: Default logical channel settings.
  * @lcla: Space for one dst src pair for logical channel transfers.
  * @lcpa: Pointer to dst and src lcpa settings.
+ * @runtime_addr: runtime configured address.
+ * @runtime_direction: runtime configured direction.
  *
  * This struct can either "be" a logical or a physical channel.
  */
@@ -200,6 +200,7 @@ struct d40_chan {
        struct dma_chan                  chan;
        struct tasklet_struct            tasklet;
        struct list_head                 client;
+       struct list_head                 pending_queue;
        struct list_head                 active;
        struct list_head                 queue;
        struct stedma40_chan_cfg         dma_cfg;
@@ -645,7 +646,20 @@ static struct d40_desc *d40_first_active_get(struct d40_chan *d40c)
 
 static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc)
 {
-       list_add_tail(&desc->node, &d40c->queue);
+       list_add_tail(&desc->node, &d40c->pending_queue);
+}
+
+static struct d40_desc *d40_first_pending(struct d40_chan *d40c)
+{
+       struct d40_desc *d;
+
+       if (list_empty(&d40c->pending_queue))
+               return NULL;
+
+       d = list_first_entry(&d40c->pending_queue,
+                            struct d40_desc,
+                            node);
+       return d;
 }
 
 static struct d40_desc *d40_first_queued(struct d40_chan *d40c)
@@ -802,6 +816,11 @@ static void d40_term_all(struct d40_chan *d40c)
                d40_desc_free(d40c, d40d);
        }
 
+       /* Release pending descriptors */
+       while ((d40d = d40_first_pending(d40c))) {
+               d40_desc_remove(d40d);
+               d40_desc_free(d40c, d40d);
+       }
 
        d40c->pending_tx = 0;
        d40c->busy = false;
@@ -2092,7 +2111,7 @@ dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr,
        struct scatterlist *sg;
        int i;
 
-       sg = kcalloc(periods + 1, sizeof(struct scatterlist), GFP_KERNEL);
+       sg = kcalloc(periods + 1, sizeof(struct scatterlist), GFP_NOWAIT);
        for (i = 0; i < periods; i++) {
                sg_dma_address(&sg[i]) = dma_addr;
                sg_dma_len(&sg[i]) = period_len;
@@ -2152,24 +2171,87 @@ static void d40_issue_pending(struct dma_chan *chan)
 
        spin_lock_irqsave(&d40c->lock, flags);
 
-       /* Busy means that pending jobs are already being processed */
+       list_splice_tail_init(&d40c->pending_queue, &d40c->queue);
+
+       /* Busy means that queued jobs are already being processed */
        if (!d40c->busy)
                (void) d40_queue_start(d40c);
 
        spin_unlock_irqrestore(&d40c->lock, flags);
 }
 
+static int
+dma40_config_to_halfchannel(struct d40_chan *d40c,
+                           struct stedma40_half_channel_info *info,
+                           enum dma_slave_buswidth width,
+                           u32 maxburst)
+{
+       enum stedma40_periph_data_width addr_width;
+       int psize;
+
+       switch (width) {
+       case DMA_SLAVE_BUSWIDTH_1_BYTE:
+               addr_width = STEDMA40_BYTE_WIDTH;
+               break;
+       case DMA_SLAVE_BUSWIDTH_2_BYTES:
+               addr_width = STEDMA40_HALFWORD_WIDTH;
+               break;
+       case DMA_SLAVE_BUSWIDTH_4_BYTES:
+               addr_width = STEDMA40_WORD_WIDTH;
+               break;
+       case DMA_SLAVE_BUSWIDTH_8_BYTES:
+               addr_width = STEDMA40_DOUBLEWORD_WIDTH;
+               break;
+       default:
+               dev_err(d40c->base->dev,
+                       "illegal peripheral address width "
+                       "requested (%d)\n",
+                       width);
+               return -EINVAL;
+       }
+
+       if (chan_is_logical(d40c)) {
+               if (maxburst >= 16)
+                       psize = STEDMA40_PSIZE_LOG_16;
+               else if (maxburst >= 8)
+                       psize = STEDMA40_PSIZE_LOG_8;
+               else if (maxburst >= 4)
+                       psize = STEDMA40_PSIZE_LOG_4;
+               else
+                       psize = STEDMA40_PSIZE_LOG_1;
+       } else {
+               if (maxburst >= 16)
+                       psize = STEDMA40_PSIZE_PHY_16;
+               else if (maxburst >= 8)
+                       psize = STEDMA40_PSIZE_PHY_8;
+               else if (maxburst >= 4)
+                       psize = STEDMA40_PSIZE_PHY_4;
+               else
+                       psize = STEDMA40_PSIZE_PHY_1;
+       }
+
+       info->data_width = addr_width;
+       info->psize = psize;
+       info->flow_ctrl = STEDMA40_NO_FLOW_CTRL;
+
+       return 0;
+}
+
 /* Runtime reconfiguration extension */
-static void d40_set_runtime_config(struct dma_chan *chan,
-                              struct dma_slave_config *config)
+static int d40_set_runtime_config(struct dma_chan *chan,
+                                 struct dma_slave_config *config)
 {
        struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
        struct stedma40_chan_cfg *cfg = &d40c->dma_cfg;
-       enum dma_slave_buswidth config_addr_width;
+       enum dma_slave_buswidth src_addr_width, dst_addr_width;
        dma_addr_t config_addr;
-       u32 config_maxburst;
-       enum stedma40_periph_data_width addr_width;
-       int psize;
+       u32 src_maxburst, dst_maxburst;
+       int ret;
+
+       src_addr_width = config->src_addr_width;
+       src_maxburst = config->src_maxburst;
+       dst_addr_width = config->dst_addr_width;
+       dst_maxburst = config->dst_maxburst;
 
        if (config->direction == DMA_FROM_DEVICE) {
                dma_addr_t dev_addr_rx =
@@ -2188,8 +2270,11 @@ static void d40_set_runtime_config(struct dma_chan *chan,
                                cfg->dir);
                cfg->dir = STEDMA40_PERIPH_TO_MEM;
 
-               config_addr_width = config->src_addr_width;
-               config_maxburst = config->src_maxburst;
+               /* Configure the memory side */
+               if (dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
+                       dst_addr_width = src_addr_width;
+               if (dst_maxburst == 0)
+                       dst_maxburst = src_maxburst;
 
        } else if (config->direction == DMA_TO_DEVICE) {
                dma_addr_t dev_addr_tx =
@@ -2208,68 +2293,39 @@ static void d40_set_runtime_config(struct dma_chan *chan,
                                cfg->dir);
                cfg->dir = STEDMA40_MEM_TO_PERIPH;
 
-               config_addr_width = config->dst_addr_width;
-               config_maxburst = config->dst_maxburst;
-
+               /* Configure the memory side */
+               if (src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
+                       src_addr_width = dst_addr_width;
+               if (src_maxburst == 0)
+                       src_maxburst = dst_maxburst;
        } else {
                dev_err(d40c->base->dev,
                        "unrecognized channel direction %d\n",
                        config->direction);
-               return;
+               return -EINVAL;
        }
 
-       switch (config_addr_width) {
-       case DMA_SLAVE_BUSWIDTH_1_BYTE:
-               addr_width = STEDMA40_BYTE_WIDTH;
-               break;
-       case DMA_SLAVE_BUSWIDTH_2_BYTES:
-               addr_width = STEDMA40_HALFWORD_WIDTH;
-               break;
-       case DMA_SLAVE_BUSWIDTH_4_BYTES:
-               addr_width = STEDMA40_WORD_WIDTH;
-               break;
-       case DMA_SLAVE_BUSWIDTH_8_BYTES:
-               addr_width = STEDMA40_DOUBLEWORD_WIDTH;
-               break;
-       default:
+       if (src_maxburst * src_addr_width != dst_maxburst * dst_addr_width) {
                dev_err(d40c->base->dev,
-                       "illegal peripheral address width "
-                       "requested (%d)\n",
-                       config->src_addr_width);
-               return;
+                       "src/dst width/maxburst mismatch: %d*%d != %d*%d\n",
+                       src_maxburst,
+                       src_addr_width,
+                       dst_maxburst,
+                       dst_addr_width);
+               return -EINVAL;
        }
 
-       if (chan_is_logical(d40c)) {
-               if (config_maxburst >= 16)
-                       psize = STEDMA40_PSIZE_LOG_16;
-               else if (config_maxburst >= 8)
-                       psize = STEDMA40_PSIZE_LOG_8;
-               else if (config_maxburst >= 4)
-                       psize = STEDMA40_PSIZE_LOG_4;
-               else
-                       psize = STEDMA40_PSIZE_LOG_1;
-       } else {
-               if (config_maxburst >= 16)
-                       psize = STEDMA40_PSIZE_PHY_16;
-               else if (config_maxburst >= 8)
-                       psize = STEDMA40_PSIZE_PHY_8;
-               else if (config_maxburst >= 4)
-                       psize = STEDMA40_PSIZE_PHY_4;
-               else if (config_maxburst >= 2)
-                       psize = STEDMA40_PSIZE_PHY_2;
-               else
-                       psize = STEDMA40_PSIZE_PHY_1;
-       }
+       ret = dma40_config_to_halfchannel(d40c, &cfg->src_info,
+                                         src_addr_width,
+                                         src_maxburst);
+       if (ret)
+               return ret;
 
-       /* Set up all the endpoint configs */
-       cfg->src_info.data_width = addr_width;
-       cfg->src_info.psize = psize;
-       cfg->src_info.big_endian = false;
-       cfg->src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
-       cfg->dst_info.data_width = addr_width;
-       cfg->dst_info.psize = psize;
-       cfg->dst_info.big_endian = false;
-       cfg->dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
+       ret = dma40_config_to_halfchannel(d40c, &cfg->dst_info,
+                                         dst_addr_width,
+                                         dst_maxburst);
+       if (ret)
+               return ret;
 
        /* Fill in register values */
        if (chan_is_logical(d40c))
@@ -2282,12 +2338,14 @@ static void d40_set_runtime_config(struct dma_chan *chan,
        d40c->runtime_addr = config_addr;
        d40c->runtime_direction = config->direction;
        dev_dbg(d40c->base->dev,
-               "configured channel %s for %s, data width %d, "
-               "maxburst %d bytes, LE, no flow control\n",
+               "configured channel %s for %s, data width %d/%d, "
+               "maxburst %d/%d elements, LE, no flow control\n",
                dma_chan_name(chan),
                (config->direction == DMA_FROM_DEVICE) ? "RX" : "TX",
-               config_addr_width,
-               config_maxburst);
+               src_addr_width, dst_addr_width,
+               src_maxburst, dst_maxburst);
+
+       return 0;
 }
 
 static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
@@ -2308,9 +2366,8 @@ static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        case DMA_RESUME:
                return d40_resume(d40c);
        case DMA_SLAVE_CONFIG:
-               d40_set_runtime_config(chan,
+               return d40_set_runtime_config(chan,
                        (struct dma_slave_config *) arg);
-               return 0;
        default:
                break;
        }
@@ -2341,6 +2398,7 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma,
 
                INIT_LIST_HEAD(&d40c->active);
                INIT_LIST_HEAD(&d40c->queue);
+               INIT_LIST_HEAD(&d40c->pending_queue);
                INIT_LIST_HEAD(&d40c->client);
 
                tasklet_init(&d40c->tasklet, dma_tasklet,
@@ -2502,25 +2560,6 @@ static int __init d40_phy_res_init(struct d40_base *base)
 
 static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
 {
-       static const struct d40_reg_val dma_id_regs[] = {
-               /* Peripheral Id */
-               { .reg = D40_DREG_PERIPHID0, .val = 0x0040},
-               { .reg = D40_DREG_PERIPHID1, .val = 0x0000},
-               /*
-                * D40_DREG_PERIPHID2 Depends on HW revision:
-                *  DB8500ed has 0x0008,
-                *  ? has 0x0018,
-                *  DB8500v1 has 0x0028
-                *  DB8500v2 has 0x0038
-                */
-               { .reg = D40_DREG_PERIPHID3, .val = 0x0000},
-
-               /* PCell Id */
-               { .reg = D40_DREG_CELLID0, .val = 0x000d},
-               { .reg = D40_DREG_CELLID1, .val = 0x00f0},
-               { .reg = D40_DREG_CELLID2, .val = 0x0005},
-               { .reg = D40_DREG_CELLID3, .val = 0x00b1}
-       };
        struct stedma40_platform_data *plat_data;
        struct clk *clk = NULL;
        void __iomem *virtbase = NULL;
@@ -2529,8 +2568,9 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
        int num_log_chans = 0;
        int num_phy_chans;
        int i;
-       u32 val;
-       u32 rev;
+       u32 pid;
+       u32 cid;
+       u8 rev;
 
        clk = clk_get(&pdev->dev, NULL);
 
@@ -2554,32 +2594,32 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
        if (!virtbase)
                goto failure;
 
-       /* HW version check */
-       for (i = 0; i < ARRAY_SIZE(dma_id_regs); i++) {
-               if (dma_id_regs[i].val !=
-                   readl(virtbase + dma_id_regs[i].reg)) {
-                       d40_err(&pdev->dev,
-                               "Unknown hardware! Expected 0x%x at 0x%x but got 0x%x\n",
-                               dma_id_regs[i].val,
-                               dma_id_regs[i].reg,
-                               readl(virtbase + dma_id_regs[i].reg));
-                       goto failure;
-               }
-       }
+       /* This is just a regular AMBA PrimeCell ID actually */
+       for (pid = 0, i = 0; i < 4; i++)
+               pid |= (readl(virtbase + resource_size(res) - 0x20 + 4 * i)
+                       & 255) << (i * 8);
+       for (cid = 0, i = 0; i < 4; i++)
+               cid |= (readl(virtbase + resource_size(res) - 0x10 + 4 * i)
+                       & 255) << (i * 8);
 
-       /* Get silicon revision and designer */
-       val = readl(virtbase + D40_DREG_PERIPHID2);
-
-       if ((val & D40_DREG_PERIPHID2_DESIGNER_MASK) !=
-           D40_HW_DESIGNER) {
+       if (cid != AMBA_CID) {
+               d40_err(&pdev->dev, "Unknown hardware! No PrimeCell ID\n");
+               goto failure;
+       }
+       if (AMBA_MANF_BITS(pid) != AMBA_VENDOR_ST) {
                d40_err(&pdev->dev, "Unknown designer! Got %x wanted %x\n",
-                       val & D40_DREG_PERIPHID2_DESIGNER_MASK,
-                       D40_HW_DESIGNER);
+                       AMBA_MANF_BITS(pid),
+                       AMBA_VENDOR_ST);
                goto failure;
        }
-
-       rev = (val & D40_DREG_PERIPHID2_REV_MASK) >>
-               D40_DREG_PERIPHID2_REV_POS;
+       /*
+        * HW revision:
+        * DB8500ed has revision 0
+        * ? has revision 1
+        * DB8500v1 has revision 2
+        * DB8500v2 has revision 3
+        */
+       rev = AMBA_REV_BITS(pid);
 
        /* The number of physical channels on this HW */
        num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4;
index 195ee65ee7f3513111f212c3b51a69bd92d96a67..b44c455158de3461f31ee5a0b0594cb136b2ad6a 100644 (file)
 #define D40_DREG_PERIPHID0     0xFE0
 #define D40_DREG_PERIPHID1     0xFE4
 #define D40_DREG_PERIPHID2     0xFE8
-#define D40_DREG_PERIPHID2_REV_POS 4
-#define D40_DREG_PERIPHID2_REV_MASK (0xf << D40_DREG_PERIPHID2_REV_POS)
-#define D40_DREG_PERIPHID2_DESIGNER_MASK 0xf
 #define D40_DREG_PERIPHID3     0xFEC
 #define D40_DREG_CELLID0       0xFF0
 #define D40_DREG_CELLID1       0xFF4
index 30da70d06a6dead6276d8fde97ed5aff36d85cda..cdae207028a720f5080b3a55c9cf63e3e7b84424 100644 (file)
@@ -45,13 +45,13 @@ static int __init pci_eisa_init(struct pci_dev *pdev,
        return 0;
 }
 
-static struct pci_device_id __initdata pci_eisa_pci_tbl[] = {
+static struct pci_device_id pci_eisa_pci_tbl[] = {
        { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
          PCI_CLASS_BRIDGE_EISA << 8, 0xffff00, 0 },
        { 0, }
 };
 
-static struct pci_driver __initdata pci_eisa_driver = {
+static struct pci_driver __refdata pci_eisa_driver = {
        .name           = "pci_eisa",
        .id_table       = pci_eisa_pci_tbl,
        .probe          = pci_eisa_init,
index 5f29aafd44624e8ee1ccb9215735b9068013e056..eb80b549ed8d17dc0d46a0d6d85a9ccbee0a25d0 100644 (file)
@@ -78,6 +78,7 @@
 #include <linux/kobject.h>
 #include <linux/device.h>
 #include <linux/slab.h>
+#include <linux/pstore.h>
 
 #include <asm/uaccess.h>
 
@@ -89,6 +90,8 @@ MODULE_DESCRIPTION("sysfs interface to EFI Variables");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(EFIVARS_VERSION);
 
+#define DUMP_NAME_LEN 52
+
 /*
  * The maximum size of VariableName + Data = 1024
  * Therefore, it's reasonable to save that much
@@ -119,6 +122,10 @@ struct efivar_attribute {
        ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count);
 };
 
+#define PSTORE_EFI_ATTRIBUTES \
+       (EFI_VARIABLE_NON_VOLATILE | \
+        EFI_VARIABLE_BOOTSERVICE_ACCESS | \
+        EFI_VARIABLE_RUNTIME_ACCESS)
 
 #define EFIVAR_ATTR(_name, _mode, _show, _store) \
 struct efivar_attribute efivar_attr_##_name = { \
@@ -141,38 +148,72 @@ efivar_create_sysfs_entry(struct efivars *efivars,
 
 /* Return the number of unicode characters in data */
 static unsigned long
-utf8_strlen(efi_char16_t *data, unsigned long maxlength)
+utf16_strnlen(efi_char16_t *s, size_t maxlength)
 {
        unsigned long length = 0;
 
-       while (*data++ != 0 && length < maxlength)
+       while (*s++ != 0 && length < maxlength)
                length++;
        return length;
 }
 
+static inline unsigned long
+utf16_strlen(efi_char16_t *s)
+{
+       return utf16_strnlen(s, ~0UL);
+}
+
 /*
  * Return the number of bytes is the length of this string
  * Note: this is NOT the same as the number of unicode characters
  */
 static inline unsigned long
-utf8_strsize(efi_char16_t *data, unsigned long maxlength)
+utf16_strsize(efi_char16_t *data, unsigned long maxlength)
 {
-       return utf8_strlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
+       return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
+}
+
+static inline int
+utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len)
+{
+       while (1) {
+               if (len == 0)
+                       return 0;
+               if (*a < *b)
+                       return -1;
+               if (*a > *b)
+                       return 1;
+               if (*a == 0) /* implies *b == 0 */
+                       return 0;
+               a++;
+               b++;
+               len--;
+       }
 }
 
 static efi_status_t
-get_var_data(struct efivars *efivars, struct efi_variable *var)
+get_var_data_locked(struct efivars *efivars, struct efi_variable *var)
 {
        efi_status_t status;
 
-       spin_lock(&efivars->lock);
        var->DataSize = 1024;
        status = efivars->ops->get_variable(var->VariableName,
                                            &var->VendorGuid,
                                            &var->Attributes,
                                            &var->DataSize,
                                            var->Data);
+       return status;
+}
+
+static efi_status_t
+get_var_data(struct efivars *efivars, struct efi_variable *var)
+{
+       efi_status_t status;
+
+       spin_lock(&efivars->lock);
+       status = get_var_data_locked(efivars, var);
        spin_unlock(&efivars->lock);
+
        if (status != EFI_SUCCESS) {
                printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
                        status);
@@ -387,12 +428,180 @@ static struct kobj_type efivar_ktype = {
        .default_attrs = def_attrs,
 };
 
+static struct pstore_info efi_pstore_info;
+
 static inline void
 efivar_unregister(struct efivar_entry *var)
 {
        kobject_put(&var->kobj);
 }
 
+#ifdef CONFIG_PSTORE
+
+static int efi_pstore_open(struct pstore_info *psi)
+{
+       struct efivars *efivars = psi->data;
+
+       spin_lock(&efivars->lock);
+       efivars->walk_entry = list_first_entry(&efivars->list,
+                                              struct efivar_entry, list);
+       return 0;
+}
+
+static int efi_pstore_close(struct pstore_info *psi)
+{
+       struct efivars *efivars = psi->data;
+
+       spin_unlock(&efivars->lock);
+       return 0;
+}
+
+static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
+                              struct timespec *timespec, struct pstore_info *psi)
+{
+       efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
+       struct efivars *efivars = psi->data;
+       char name[DUMP_NAME_LEN];
+       int i;
+       unsigned int part, size;
+       unsigned long time;
+
+       while (&efivars->walk_entry->list != &efivars->list) {
+               if (!efi_guidcmp(efivars->walk_entry->var.VendorGuid,
+                                vendor)) {
+                       for (i = 0; i < DUMP_NAME_LEN; i++) {
+                               name[i] = efivars->walk_entry->var.VariableName[i];
+                       }
+                       if (sscanf(name, "dump-type%u-%u-%lu", type, &part, &time) == 3) {
+                               *id = part;
+                               timespec->tv_sec = time;
+                               timespec->tv_nsec = 0;
+                               get_var_data_locked(efivars, &efivars->walk_entry->var);
+                               size = efivars->walk_entry->var.DataSize;
+                               memcpy(psi->buf, efivars->walk_entry->var.Data, size);
+                               efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
+                                                  struct efivar_entry, list);
+                               return size;
+                       }
+               }
+               efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
+                                                struct efivar_entry, list);
+       }
+       return 0;
+}
+
+static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
+                           size_t size, struct pstore_info *psi)
+{
+       char name[DUMP_NAME_LEN];
+       char stub_name[DUMP_NAME_LEN];
+       efi_char16_t efi_name[DUMP_NAME_LEN];
+       efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
+       struct efivars *efivars = psi->data;
+       struct efivar_entry *entry, *found = NULL;
+       int i;
+
+       sprintf(stub_name, "dump-type%u-%u-", type, part);
+       sprintf(name, "%s%lu", stub_name, get_seconds());
+
+       spin_lock(&efivars->lock);
+
+       for (i = 0; i < DUMP_NAME_LEN; i++)
+               efi_name[i] = stub_name[i];
+
+       /*
+        * Clean up any entries with the same name
+        */
+
+       list_for_each_entry(entry, &efivars->list, list) {
+               get_var_data_locked(efivars, &entry->var);
+
+               if (efi_guidcmp(entry->var.VendorGuid, vendor))
+                       continue;
+               if (utf16_strncmp(entry->var.VariableName, efi_name,
+                                 utf16_strlen(efi_name)))
+                       continue;
+               /* Needs to be a prefix */
+               if (entry->var.VariableName[utf16_strlen(efi_name)] == 0)
+                       continue;
+
+               /* found */
+               found = entry;
+               efivars->ops->set_variable(entry->var.VariableName,
+                                          &entry->var.VendorGuid,
+                                          PSTORE_EFI_ATTRIBUTES,
+                                          0, NULL);
+       }
+
+       if (found)
+               list_del(&found->list);
+
+       for (i = 0; i < DUMP_NAME_LEN; i++)
+               efi_name[i] = name[i];
+
+       efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES,
+                                  size, psi->buf);
+
+       spin_unlock(&efivars->lock);
+
+       if (found)
+               efivar_unregister(found);
+
+       if (size)
+               efivar_create_sysfs_entry(efivars,
+                                         utf16_strsize(efi_name,
+                                                       DUMP_NAME_LEN * 2),
+                                         efi_name, &vendor);
+
+       return part;
+};
+
+static int efi_pstore_erase(enum pstore_type_id type, u64 id,
+                           struct pstore_info *psi)
+{
+       efi_pstore_write(type, id, 0, psi);
+
+       return 0;
+}
+#else
+static int efi_pstore_open(struct pstore_info *psi)
+{
+       return 0;
+}
+
+static int efi_pstore_close(struct pstore_info *psi)
+{
+       return 0;
+}
+
+static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
+                              struct timespec *time, struct pstore_info *psi)
+{
+       return -1;
+}
+
+static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
+                           size_t size, struct pstore_info *psi)
+{
+       return 0;
+}
+
+static int efi_pstore_erase(enum pstore_type_id type, u64 id,
+                           struct pstore_info *psi)
+{
+       return 0;
+}
+#endif
+
+static struct pstore_info efi_pstore_info = {
+       .owner          = THIS_MODULE,
+       .name           = "efi",
+       .open           = efi_pstore_open,
+       .close          = efi_pstore_close,
+       .read           = efi_pstore_read,
+       .write          = efi_pstore_write,
+       .erase          = efi_pstore_erase,
+};
 
 static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
                             struct bin_attribute *bin_attr,
@@ -414,8 +623,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
         * Does this variable already exist?
         */
        list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
-               strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
-               strsize2 = utf8_strsize(new_var->VariableName, 1024);
+               strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
+               strsize2 = utf16_strsize(new_var->VariableName, 1024);
                if (strsize1 == strsize2 &&
                        !memcmp(&(search_efivar->var.VariableName),
                                new_var->VariableName, strsize1) &&
@@ -447,8 +656,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
 
        /* Create the entry in sysfs.  Locking is not required here */
        status = efivar_create_sysfs_entry(efivars,
-                                          utf8_strsize(new_var->VariableName,
-                                                       1024),
+                                          utf16_strsize(new_var->VariableName,
+                                                        1024),
                                           new_var->VariableName,
                                           &new_var->VendorGuid);
        if (status) {
@@ -477,8 +686,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
         * Does this variable already exist?
         */
        list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
-               strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
-               strsize2 = utf8_strsize(del_var->VariableName, 1024);
+               strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
+               strsize2 = utf16_strsize(del_var->VariableName, 1024);
                if (strsize1 == strsize2 &&
                        !memcmp(&(search_efivar->var.VariableName),
                                del_var->VariableName, strsize1) &&
@@ -763,6 +972,16 @@ int register_efivars(struct efivars *efivars,
        if (error)
                unregister_efivars(efivars);
 
+       efivars->efi_pstore_info = efi_pstore_info;
+
+       efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
+       if (efivars->efi_pstore_info.buf) {
+               efivars->efi_pstore_info.bufsize = 1024;
+               efivars->efi_pstore_info.data = efivars;
+               mutex_init(&efivars->efi_pstore_info.buf_mutex);
+               pstore_register(&efivars->efi_pstore_info);
+       }
+
 out:
        kfree(variable_name);
 
index 9d8c892d07c93b1d4e855adc839e28bea5959aab..9d2668a5087258e4f0624f69e40d9ccfbc989e0b 100644 (file)
@@ -90,7 +90,6 @@ int drm_debugfs_create_files(struct drm_info_list *files, int count,
        struct drm_device *dev = minor->dev;
        struct dentry *ent;
        struct drm_info_node *tmp;
-       char name[64];
        int i, ret;
 
        for (i = 0; i < count; i++) {
@@ -108,6 +107,9 @@ int drm_debugfs_create_files(struct drm_info_list *files, int count,
                ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO,
                                          root, tmp, &drm_debugfs_fops);
                if (!ent) {
+                       char name[64];
+                       strncpy(name, root->d_name.name,
+                                               min(root->d_name.len, 64U));
                        DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/%s\n",
                                  name, files[i].name);
                        kfree(tmp);
index 756af4d7ec74048f4776e7d9552fffb678f88770..7425e5c9bd75a0f8d7f0f9d299402fc8111d78f6 100644 (file)
@@ -127,6 +127,23 @@ static const u8 edid_header[] = {
        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
 };
 
+ /*
+ * Sanity check the header of the base EDID block.  Return 8 if the header
+ * is perfect, down to 0 if it's totally wrong.
+ */
+int drm_edid_header_is_valid(const u8 *raw_edid)
+{
+       int i, score = 0;
+
+       for (i = 0; i < sizeof(edid_header); i++)
+               if (raw_edid[i] == edid_header[i])
+                       score++;
+
+       return score;
+}
+EXPORT_SYMBOL(drm_edid_header_is_valid);
+
+
 /*
  * Sanity check the EDID block (base or extension).  Return 0 if the block
  * doesn't check out, or 1 if it's valid.
@@ -139,12 +156,7 @@ drm_edid_block_valid(u8 *raw_edid)
        struct edid *edid = (struct edid *)raw_edid;
 
        if (raw_edid[0] == 0x00) {
-               int score = 0;
-
-               for (i = 0; i < sizeof(edid_header); i++)
-                       if (raw_edid[i] == edid_header[i])
-                               score++;
-
+               int score = drm_edid_header_is_valid(raw_edid);
                if (score == 8) ;
                else if (score >= 6) {
                        DRM_DEBUG("Fixing EDID header, your hardware may be failing\n");
@@ -1439,6 +1451,8 @@ EXPORT_SYMBOL(drm_detect_monitor_audio);
 static void drm_add_display_info(struct edid *edid,
                                 struct drm_display_info *info)
 {
+       u8 *edid_ext;
+
        info->width_mm = edid->width_cm * 10;
        info->height_mm = edid->height_cm * 10;
 
@@ -1483,6 +1497,13 @@ static void drm_add_display_info(struct edid *edid,
                info->color_formats = DRM_COLOR_FORMAT_YCRCB444;
        if (info->color_formats & DRM_EDID_FEATURE_RGB_YCRCB422)
                info->color_formats = DRM_COLOR_FORMAT_YCRCB422;
+
+       /* Get data from CEA blocks if present */
+       edid_ext = drm_find_cea_extension(edid);
+       if (!edid_ext)
+               return;
+
+       info->cea_rev = edid_ext[1];
 }
 
 /**
index 2022a5c966bb050d89084a699cc6e6ab8508a026..3830e9e478c0c4af57e2983181bfc545c854ba45 100644 (file)
@@ -291,11 +291,14 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state)
        if (!dev->irq_enabled)
                return;
 
-       if (state)
-               dev->driver->irq_uninstall(dev);
-       else {
-               dev->driver->irq_preinstall(dev);
-               dev->driver->irq_postinstall(dev);
+       if (state) {
+               if (dev->driver->irq_uninstall)
+                       dev->driver->irq_uninstall(dev);
+       } else {
+               if (dev->driver->irq_preinstall)
+                       dev->driver->irq_preinstall(dev);
+               if (dev->driver->irq_postinstall)
+                       dev->driver->irq_postinstall(dev);
        }
 }
 
@@ -338,7 +341,8 @@ int drm_irq_install(struct drm_device *dev)
        DRM_DEBUG("irq=%d\n", drm_dev_to_irq(dev));
 
        /* Before installing handler */
-       dev->driver->irq_preinstall(dev);
+       if (dev->driver->irq_preinstall)
+               dev->driver->irq_preinstall(dev);
 
        /* Install handler */
        if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED))
@@ -363,11 +367,16 @@ int drm_irq_install(struct drm_device *dev)
                vga_client_register(dev->pdev, (void *)dev, drm_irq_vgaarb_nokms, NULL);
 
        /* After installing handler */
-       ret = dev->driver->irq_postinstall(dev);
+       if (dev->driver->irq_postinstall)
+               ret = dev->driver->irq_postinstall(dev);
+
        if (ret < 0) {
                mutex_lock(&dev->struct_mutex);
                dev->irq_enabled = 0;
                mutex_unlock(&dev->struct_mutex);
+               if (!drm_core_check_feature(dev, DRIVER_MODESET))
+                       vga_client_register(dev->pdev, NULL, NULL, NULL);
+               free_irq(drm_dev_to_irq(dev), dev);
        }
 
        return ret;
@@ -413,7 +422,8 @@ int drm_irq_uninstall(struct drm_device *dev)
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                vga_client_register(dev->pdev, NULL, NULL, NULL);
 
-       dev->driver->irq_uninstall(dev);
+       if (dev->driver->irq_uninstall)
+               dev->driver->irq_uninstall(dev);
 
        free_irq(drm_dev_to_irq(dev), dev);
 
index e2662497d50f582d32bf94abda2f2ff2adf9978f..a8ab6263e0d75b357d1643a5e009de6e59494307 100644 (file)
@@ -1338,6 +1338,155 @@ static const struct file_operations i915_wedged_fops = {
        .llseek = default_llseek,
 };
 
+static int
+i915_max_freq_open(struct inode *inode,
+                  struct file *filp)
+{
+       filp->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t
+i915_max_freq_read(struct file *filp,
+                  char __user *ubuf,
+                  size_t max,
+                  loff_t *ppos)
+{
+       struct drm_device *dev = filp->private_data;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       char buf[80];
+       int len;
+
+       len = snprintf(buf, sizeof (buf),
+                      "max freq: %d\n", dev_priv->max_delay * 50);
+
+       if (len > sizeof (buf))
+               len = sizeof (buf);
+
+       return simple_read_from_buffer(ubuf, max, ppos, buf, len);
+}
+
+static ssize_t
+i915_max_freq_write(struct file *filp,
+                 const char __user *ubuf,
+                 size_t cnt,
+                 loff_t *ppos)
+{
+       struct drm_device *dev = filp->private_data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       char buf[20];
+       int val = 1;
+
+       if (cnt > 0) {
+               if (cnt > sizeof (buf) - 1)
+                       return -EINVAL;
+
+               if (copy_from_user(buf, ubuf, cnt))
+                       return -EFAULT;
+               buf[cnt] = 0;
+
+               val = simple_strtoul(buf, NULL, 0);
+       }
+
+       DRM_DEBUG_DRIVER("Manually setting max freq to %d\n", val);
+
+       /*
+        * Turbo will still be enabled, but won't go above the set value.
+        */
+       dev_priv->max_delay = val / 50;
+
+       gen6_set_rps(dev, val / 50);
+
+       return cnt;
+}
+
+static const struct file_operations i915_max_freq_fops = {
+       .owner = THIS_MODULE,
+       .open = i915_max_freq_open,
+       .read = i915_max_freq_read,
+       .write = i915_max_freq_write,
+       .llseek = default_llseek,
+};
+
+static int
+i915_cache_sharing_open(struct inode *inode,
+                  struct file *filp)
+{
+       filp->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t
+i915_cache_sharing_read(struct file *filp,
+                  char __user *ubuf,
+                  size_t max,
+                  loff_t *ppos)
+{
+       struct drm_device *dev = filp->private_data;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       char buf[80];
+       u32 snpcr;
+       int len;
+
+       mutex_lock(&dev_priv->dev->struct_mutex);
+       snpcr = I915_READ(GEN6_MBCUNIT_SNPCR);
+       mutex_unlock(&dev_priv->dev->struct_mutex);
+
+       len = snprintf(buf, sizeof (buf),
+                      "%d\n", (snpcr & GEN6_MBC_SNPCR_MASK) >>
+                      GEN6_MBC_SNPCR_SHIFT);
+
+       if (len > sizeof (buf))
+               len = sizeof (buf);
+
+       return simple_read_from_buffer(ubuf, max, ppos, buf, len);
+}
+
+static ssize_t
+i915_cache_sharing_write(struct file *filp,
+                 const char __user *ubuf,
+                 size_t cnt,
+                 loff_t *ppos)
+{
+       struct drm_device *dev = filp->private_data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       char buf[20];
+       u32 snpcr;
+       int val = 1;
+
+       if (cnt > 0) {
+               if (cnt > sizeof (buf) - 1)
+                       return -EINVAL;
+
+               if (copy_from_user(buf, ubuf, cnt))
+                       return -EFAULT;
+               buf[cnt] = 0;
+
+               val = simple_strtoul(buf, NULL, 0);
+       }
+
+       if (val < 0 || val > 3)
+               return -EINVAL;
+
+       DRM_DEBUG_DRIVER("Manually setting uncore sharing to %d\n", val);
+
+       /* Update the cache sharing policy here as well */
+       snpcr = I915_READ(GEN6_MBCUNIT_SNPCR);
+       snpcr &= ~GEN6_MBC_SNPCR_MASK;
+       snpcr |= (val << GEN6_MBC_SNPCR_SHIFT);
+       I915_WRITE(GEN6_MBCUNIT_SNPCR, snpcr);
+
+       return cnt;
+}
+
+static const struct file_operations i915_cache_sharing_fops = {
+       .owner = THIS_MODULE,
+       .open = i915_cache_sharing_open,
+       .read = i915_cache_sharing_read,
+       .write = i915_cache_sharing_write,
+       .llseek = default_llseek,
+};
+
 /* As the drm_debugfs_init() routines are called before dev->dev_private is
  * allocated we need to hook into the minor for release. */
 static int
@@ -1437,6 +1586,36 @@ static int i915_forcewake_create(struct dentry *root, struct drm_minor *minor)
        return drm_add_fake_info_node(minor, ent, &i915_forcewake_fops);
 }
 
+static int i915_max_freq_create(struct dentry *root, struct drm_minor *minor)
+{
+       struct drm_device *dev = minor->dev;
+       struct dentry *ent;
+
+       ent = debugfs_create_file("i915_max_freq",
+                                 S_IRUGO | S_IWUSR,
+                                 root, dev,
+                                 &i915_max_freq_fops);
+       if (IS_ERR(ent))
+               return PTR_ERR(ent);
+
+       return drm_add_fake_info_node(minor, ent, &i915_max_freq_fops);
+}
+
+static int i915_cache_sharing_create(struct dentry *root, struct drm_minor *minor)
+{
+       struct drm_device *dev = minor->dev;
+       struct dentry *ent;
+
+       ent = debugfs_create_file("i915_cache_sharing",
+                                 S_IRUGO | S_IWUSR,
+                                 root, dev,
+                                 &i915_cache_sharing_fops);
+       if (IS_ERR(ent))
+               return PTR_ERR(ent);
+
+       return drm_add_fake_info_node(minor, ent, &i915_cache_sharing_fops);
+}
+
 static struct drm_info_list i915_debugfs_list[] = {
        {"i915_capabilities", i915_capabilities, 0},
        {"i915_gem_objects", i915_gem_object_info, 0},
@@ -1488,6 +1667,12 @@ int i915_debugfs_init(struct drm_minor *minor)
                return ret;
 
        ret = i915_forcewake_create(minor->debugfs_root, minor);
+       if (ret)
+               return ret;
+       ret = i915_max_freq_create(minor->debugfs_root, minor);
+       if (ret)
+               return ret;
+       ret = i915_cache_sharing_create(minor->debugfs_root, minor);
        if (ret)
                return ret;
 
@@ -1504,6 +1689,10 @@ void i915_debugfs_cleanup(struct drm_minor *minor)
                                 1, minor);
        drm_debugfs_remove_files((struct drm_info_list *) &i915_wedged_fops,
                                 1, minor);
+       drm_debugfs_remove_files((struct drm_info_list *) &i915_max_freq_fops,
+                                1, minor);
+       drm_debugfs_remove_files((struct drm_info_list *) &i915_cache_sharing_fops,
+                                1, minor);
 }
 
 #endif /* CONFIG_DEBUG_FS */
index 12712824a6d22e818c010704e791e2ee9263c4da..8a3942c4f09961a1ce7c4e7918f7124c6e1eaa41 100644 (file)
@@ -61,7 +61,6 @@ static void i915_write_hws_pga(struct drm_device *dev)
 static int i915_init_phys_hws(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
-       struct intel_ring_buffer *ring = LP_RING(dev_priv);
 
        /* Program Hardware Status Page */
        dev_priv->status_page_dmah =
@@ -71,10 +70,9 @@ static int i915_init_phys_hws(struct drm_device *dev)
                DRM_ERROR("Can not allocate hardware status page\n");
                return -ENOMEM;
        }
-       ring->status_page.page_addr =
-               (void __force __iomem *)dev_priv->status_page_dmah->vaddr;
 
-       memset_io(ring->status_page.page_addr, 0, PAGE_SIZE);
+       memset_io((void __force __iomem *)dev_priv->status_page_dmah->vaddr,
+                 0, PAGE_SIZE);
 
        i915_write_hws_pga(dev);
 
index 6867e193d85e6f9e8b9ab07eadef040f42904624..feb4f164fd1b35264b6673201d68e0f22494f025 100644 (file)
@@ -544,6 +544,7 @@ typedef struct drm_i915_private {
        u32 savePIPEB_LINK_M1;
        u32 savePIPEB_LINK_N1;
        u32 saveMCHBAR_RENDER_STANDBY;
+       u32 savePCH_PORT_HOTPLUG;
 
        struct {
                /** Bridge to intel-gtt-ko */
index d1cd8b89f47db567407ce74f1b8f2d9f3abbce31..a546a71fb060abddd9d160abf3df61a90aa4eb33 100644 (file)
@@ -3112,7 +3112,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
 
        if (pipelined != obj->ring) {
                ret = i915_gem_object_wait_rendering(obj);
-               if (ret)
+               if (ret == -ERESTARTSYS)
                        return ret;
        }
 
index 23d1ae67d2796fa2e3936aacf2f4c054bf3c418c..02f96fd0d52dbea419cf575184196de2bbe66eb0 100644 (file)
@@ -306,12 +306,15 @@ static void i915_hotplug_work_func(struct work_struct *work)
        struct drm_mode_config *mode_config = &dev->mode_config;
        struct intel_encoder *encoder;
 
+       mutex_lock(&mode_config->mutex);
        DRM_DEBUG_KMS("running encoder hotplug functions\n");
 
        list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
                if (encoder->hot_plug)
                        encoder->hot_plug(encoder);
 
+       mutex_unlock(&mode_config->mutex);
+
        /* Just fire off a uevent and let userspace tell us what to do */
        drm_helper_hpd_irq_event(dev);
 }
index 02db299f621affee404387d15fa639b44ac1534d..d1331f771e2f2f863677cc69bcc11a06d3af0ae4 100644 (file)
 #define  GRDOM_RENDER  (1<<2)
 #define  GRDOM_MEDIA   (3<<2)
 
+#define GEN6_MBCUNIT_SNPCR     0x900c /* for LLC config */
+#define   GEN6_MBC_SNPCR_SHIFT 21
+#define   GEN6_MBC_SNPCR_MASK  (3<<21)
+#define   GEN6_MBC_SNPCR_MAX   (0<<21)
+#define   GEN6_MBC_SNPCR_MED   (1<<21)
+#define   GEN6_MBC_SNPCR_LOW   (2<<21)
+#define   GEN6_MBC_SNPCR_MIN   (3<<21) /* only 1/16th of the cache is shared */
+
 #define GEN6_GDRST     0x941c
 #define  GEN6_GRDOM_FULL               (1 << 0)
 #define  GEN6_GRDOM_RENDER             (1 << 1)
 #define   VIDEO_DIP_SELECT_AVI         (0 << 19)
 #define   VIDEO_DIP_SELECT_VENDOR      (1 << 19)
 #define   VIDEO_DIP_SELECT_SPD         (3 << 19)
+#define   VIDEO_DIP_SELECT_MASK                (3 << 19)
 #define   VIDEO_DIP_FREQ_ONCE          (0 << 16)
 #define   VIDEO_DIP_FREQ_VSYNC         (1 << 16)
 #define   VIDEO_DIP_FREQ_2VSYNC                (2 << 16)
 #define   DP_PIPEB_SELECT              (1 << 30)
 #define   DP_PIPE_MASK                 (1 << 30)
 
-#define DP_PIPE_ENABLED(V, P) \
-       (((V) & (DP_PIPE_MASK | DP_PORT_EN)) == ((P) << 30 | DP_PORT_EN))
-
 /* Link training mode - select a suitable mode for each stage */
 #define   DP_LINK_TRAIN_PAT_1          (0 << 28)
 #define   DP_LINK_TRAIN_PAT_2          (1 << 28)
 #define _TRANSA_DP_LINK_M2       0xe0048
 #define _TRANSA_DP_LINK_N2       0xe004c
 
+/* Per-transcoder DIP controls */
+
+#define _VIDEO_DIP_CTL_A         0xe0200
+#define _VIDEO_DIP_DATA_A        0xe0208
+#define _VIDEO_DIP_GCP_A         0xe0210
+
+#define _VIDEO_DIP_CTL_B         0xe1200
+#define _VIDEO_DIP_DATA_B        0xe1208
+#define _VIDEO_DIP_GCP_B         0xe1210
+
+#define TVIDEO_DIP_CTL(pipe) _PIPE(pipe, _VIDEO_DIP_CTL_A, _VIDEO_DIP_CTL_B)
+#define TVIDEO_DIP_DATA(pipe) _PIPE(pipe, _VIDEO_DIP_DATA_A, _VIDEO_DIP_DATA_B)
+#define TVIDEO_DIP_GCP(pipe) _PIPE(pipe, _VIDEO_DIP_GCP_A, _VIDEO_DIP_GCP_B)
+
 #define _TRANS_HTOTAL_B          0xe1000
 #define _TRANS_HBLANK_B          0xe1004
 #define _TRANS_HSYNC_B           0xe1008
 #define  TRANS_6BPC             (2<<5)
 #define  TRANS_12BPC            (3<<5)
 
+#define _TRANSA_CHICKEN2        0xf0064
+#define _TRANSB_CHICKEN2        0xf1064
+#define TRANS_CHICKEN2(pipe) _PIPE(pipe, _TRANSA_CHICKEN2, _TRANSB_CHICKEN2)
+#define   TRANS_AUTOTRAIN_GEN_STALL_DIS        (1<<31)
+
+#define SOUTH_CHICKEN1         0xc2000
+#define  FDIA_PHASE_SYNC_SHIFT_OVR     19
+#define  FDIA_PHASE_SYNC_SHIFT_EN      18
+#define FDI_PHASE_SYNC_OVR(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_OVR - ((pipe) * 2)))
+#define FDI_PHASE_SYNC_EN(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_EN - ((pipe) * 2)))
 #define SOUTH_CHICKEN2         0xc2004
 #define  DPLS_EDP_PPS_FIX_DIS  (1<<0)
 
index 285758603ac82e4e0378f458e3d5a8161a9f5ac7..87677d60d0df21758b9a70c51e5f23388e21a5e6 100644 (file)
@@ -812,6 +812,7 @@ int i915_save_state(struct drm_device *dev)
                dev_priv->saveFDI_RXB_IMR = I915_READ(_FDI_RXB_IMR);
                dev_priv->saveMCHBAR_RENDER_STANDBY =
                        I915_READ(RSTDBYCTL);
+               dev_priv->savePCH_PORT_HOTPLUG = I915_READ(PCH_PORT_HOTPLUG);
        } else {
                dev_priv->saveIER = I915_READ(IER);
                dev_priv->saveIMR = I915_READ(IMR);
@@ -863,6 +864,7 @@ int i915_restore_state(struct drm_device *dev)
                I915_WRITE(GTIMR, dev_priv->saveGTIMR);
                I915_WRITE(_FDI_RXA_IMR, dev_priv->saveFDI_RXA_IMR);
                I915_WRITE(_FDI_RXB_IMR, dev_priv->saveFDI_RXB_IMR);
+               I915_WRITE(PCH_PORT_HOTPLUG, dev_priv->savePCH_PORT_HOTPLUG);
        } else {
                I915_WRITE(IER, dev_priv->saveIER);
                I915_WRITE(IMR, dev_priv->saveIMR);
index 393a39922e53345b7cb0c525b7c255f0ea204f24..35364e68a09127528b1d6d6f18bae3e331d78b85 100644 (file)
@@ -980,11 +980,29 @@ static void assert_transcoder_disabled(struct drm_i915_private *dev_priv,
             pipe_name(pipe));
 }
 
+static bool dp_pipe_enabled(struct drm_i915_private *dev_priv, enum pipe pipe,
+                           int reg, u32 port_sel, u32 val)
+{
+       if ((val & DP_PORT_EN) == 0)
+               return false;
+
+       if (HAS_PCH_CPT(dev_priv->dev)) {
+               u32     trans_dp_ctl_reg = TRANS_DP_CTL(pipe);
+               u32     trans_dp_ctl = I915_READ(trans_dp_ctl_reg);
+               if ((trans_dp_ctl & TRANS_DP_PORT_SEL_MASK) != port_sel)
+                       return false;
+       } else {
+               if ((val & DP_PIPE_MASK) != (pipe << 30))
+                       return false;
+       }
+       return true;
+}
+
 static void assert_pch_dp_disabled(struct drm_i915_private *dev_priv,
-                                  enum pipe pipe, int reg)
+                                  enum pipe pipe, int reg, u32 port_sel)
 {
        u32 val = I915_READ(reg);
-       WARN(DP_PIPE_ENABLED(val, pipe),
+       WARN(dp_pipe_enabled(dev_priv, pipe, reg, port_sel, val),
             "PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n",
             reg, pipe_name(pipe));
 }
@@ -1004,9 +1022,9 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv,
        int reg;
        u32 val;
 
-       assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_B);
-       assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_C);
-       assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_D);
+       assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_B, TRANS_DP_PORT_SEL_B);
+       assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_C, TRANS_DP_PORT_SEL_C);
+       assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_D, TRANS_DP_PORT_SEL_D);
 
        reg = PCH_ADPA;
        val = I915_READ(reg);
@@ -1276,6 +1294,17 @@ static void intel_disable_pipe(struct drm_i915_private *dev_priv,
        intel_wait_for_pipe_off(dev_priv->dev, pipe);
 }
 
+/*
+ * Plane regs are double buffered, going from enabled->disabled needs a
+ * trigger in order to latch.  The display address reg provides this.
+ */
+static void intel_flush_display_plane(struct drm_i915_private *dev_priv,
+                                     enum plane plane)
+{
+       I915_WRITE(DSPADDR(plane), I915_READ(DSPADDR(plane)));
+       I915_WRITE(DSPSURF(plane), I915_READ(DSPSURF(plane)));
+}
+
 /**
  * intel_enable_plane - enable a display plane on a given pipe
  * @dev_priv: i915 private structure
@@ -1299,20 +1328,10 @@ static void intel_enable_plane(struct drm_i915_private *dev_priv,
                return;
 
        I915_WRITE(reg, val | DISPLAY_PLANE_ENABLE);
+       intel_flush_display_plane(dev_priv, plane);
        intel_wait_for_vblank(dev_priv->dev, pipe);
 }
 
-/*
- * Plane regs are double buffered, going from enabled->disabled needs a
- * trigger in order to latch.  The display address reg provides this.
- */
-static void intel_flush_display_plane(struct drm_i915_private *dev_priv,
-                                     enum plane plane)
-{
-       u32 reg = DSPADDR(plane);
-       I915_WRITE(reg, I915_READ(reg));
-}
-
 /**
  * intel_disable_plane - disable a display plane
  * @dev_priv: i915 private structure
@@ -1338,19 +1357,24 @@ static void intel_disable_plane(struct drm_i915_private *dev_priv,
 }
 
 static void disable_pch_dp(struct drm_i915_private *dev_priv,
-                          enum pipe pipe, int reg)
+                          enum pipe pipe, int reg, u32 port_sel)
 {
        u32 val = I915_READ(reg);
-       if (DP_PIPE_ENABLED(val, pipe))
+       if (dp_pipe_enabled(dev_priv, pipe, reg, port_sel, val)) {
+               DRM_DEBUG_KMS("Disabling pch dp %x on pipe %d\n", reg, pipe);
                I915_WRITE(reg, val & ~DP_PORT_EN);
+       }
 }
 
 static void disable_pch_hdmi(struct drm_i915_private *dev_priv,
                             enum pipe pipe, int reg)
 {
        u32 val = I915_READ(reg);
-       if (HDMI_PIPE_ENABLED(val, pipe))
+       if (HDMI_PIPE_ENABLED(val, pipe)) {
+               DRM_DEBUG_KMS("Disabling pch HDMI %x on pipe %d\n",
+                             reg, pipe);
                I915_WRITE(reg, val & ~PORT_ENABLE);
+       }
 }
 
 /* Disable any ports connected to this transcoder */
@@ -1362,9 +1386,9 @@ static void intel_disable_pch_ports(struct drm_i915_private *dev_priv,
        val = I915_READ(PCH_PP_CONTROL);
        I915_WRITE(PCH_PP_CONTROL, val | PANEL_UNLOCK_REGS);
 
-       disable_pch_dp(dev_priv, pipe, PCH_DP_B);
-       disable_pch_dp(dev_priv, pipe, PCH_DP_C);
-       disable_pch_dp(dev_priv, pipe, PCH_DP_D);
+       disable_pch_dp(dev_priv, pipe, PCH_DP_B, TRANS_DP_PORT_SEL_B);
+       disable_pch_dp(dev_priv, pipe, PCH_DP_C, TRANS_DP_PORT_SEL_C);
+       disable_pch_dp(dev_priv, pipe, PCH_DP_D, TRANS_DP_PORT_SEL_D);
 
        reg = PCH_ADPA;
        val = I915_READ(reg);
@@ -2096,7 +2120,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
 
        /* no fb bound */
        if (!crtc->fb) {
-               DRM_DEBUG_KMS("No FB bound\n");
+               DRM_ERROR("No FB bound\n");
                return 0;
        }
 
@@ -2105,6 +2129,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        case 1:
                break;
        default:
+               DRM_ERROR("no plane for crtc\n");
                return -EINVAL;
        }
 
@@ -2114,6 +2139,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                                         NULL);
        if (ret != 0) {
                mutex_unlock(&dev->struct_mutex);
+               DRM_ERROR("pin & fence failed\n");
                return ret;
        }
 
@@ -2142,6 +2168,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        if (ret) {
                i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj);
                mutex_unlock(&dev->struct_mutex);
+               DRM_ERROR("failed to update base address\n");
                return ret;
        }
 
@@ -2248,6 +2275,18 @@ static void intel_fdi_normal_train(struct drm_crtc *crtc)
                           FDI_FE_ERRC_ENABLE);
 }
 
+static void cpt_phase_pointer_enable(struct drm_device *dev, int pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 flags = I915_READ(SOUTH_CHICKEN1);
+
+       flags |= FDI_PHASE_SYNC_OVR(pipe);
+       I915_WRITE(SOUTH_CHICKEN1, flags); /* once to unlock... */
+       flags |= FDI_PHASE_SYNC_EN(pipe);
+       I915_WRITE(SOUTH_CHICKEN1, flags); /* then again to enable */
+       POSTING_READ(SOUTH_CHICKEN1);
+}
+
 /* The FDI link training functions for ILK/Ibexpeak. */
 static void ironlake_fdi_link_train(struct drm_crtc *crtc)
 {
@@ -2398,6 +2437,9 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
        POSTING_READ(reg);
        udelay(150);
 
+       if (HAS_PCH_CPT(dev))
+               cpt_phase_pointer_enable(dev, pipe);
+
        for (i = 0; i < 4; i++ ) {
                reg = FDI_TX_CTL(pipe);
                temp = I915_READ(reg);
@@ -2514,6 +2556,9 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
        POSTING_READ(reg);
        udelay(150);
 
+       if (HAS_PCH_CPT(dev))
+               cpt_phase_pointer_enable(dev, pipe);
+
        for (i = 0; i < 4; i++ ) {
                reg = FDI_TX_CTL(pipe);
                temp = I915_READ(reg);
@@ -2623,6 +2668,17 @@ static void ironlake_fdi_pll_enable(struct drm_crtc *crtc)
        }
 }
 
+static void cpt_phase_pointer_disable(struct drm_device *dev, int pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 flags = I915_READ(SOUTH_CHICKEN1);
+
+       flags &= ~(FDI_PHASE_SYNC_EN(pipe));
+       I915_WRITE(SOUTH_CHICKEN1, flags); /* once to disable... */
+       flags &= ~(FDI_PHASE_SYNC_OVR(pipe));
+       I915_WRITE(SOUTH_CHICKEN1, flags); /* then again to lock */
+       POSTING_READ(SOUTH_CHICKEN1);
+}
 static void ironlake_fdi_disable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -2652,6 +2708,8 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc)
                I915_WRITE(FDI_RX_CHICKEN(pipe),
                           I915_READ(FDI_RX_CHICKEN(pipe) &
                                     ~FDI_RX_PHASE_SYNC_POINTER_EN));
+       } else if (HAS_PCH_CPT(dev)) {
+               cpt_phase_pointer_disable(dev, pipe);
        }
 
        /* still set train pattern 1 */
@@ -2862,14 +2920,18 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
                I915_WRITE(PF_WIN_SZ(pipe), dev_priv->pch_pf_size);
        }
 
+       /*
+        * On ILK+ LUT must be loaded before the pipe is running but with
+        * clocks enabled
+        */
+       intel_crtc_load_lut(crtc);
+
        intel_enable_pipe(dev_priv, pipe, is_pch_port);
        intel_enable_plane(dev_priv, plane, pipe);
 
        if (is_pch_port)
                ironlake_pch_enable(crtc);
 
-       intel_crtc_load_lut(crtc);
-
        mutex_lock(&dev->struct_mutex);
        intel_update_fbc(dev);
        mutex_unlock(&dev->struct_mutex);
@@ -4538,7 +4600,9 @@ static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc,
                        if (connector->encoder != encoder)
                                continue;
 
-                       if (connector->display_info.bpc < display_bpc) {
+                       /* Don't use an invalid EDID bpc value */
+                       if (connector->display_info.bpc &&
+                           connector->display_info.bpc < display_bpc) {
                                DRM_DEBUG_DRIVER("clamping display bpc (was %d) to EDID reported max of %d\n", display_bpc, connector->display_info.bpc);
                                display_bpc = connector->display_info.bpc;
                        }
@@ -5153,7 +5217,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                temp |= PIPE_12BPC;
                break;
        default:
-               WARN(1, "intel_choose_pipe_bpp returned invalid value\n");
+               WARN(1, "intel_choose_pipe_bpp returned invalid value %d\n",
+                       pipe_bpp);
                temp |= PIPE_8BPC;
                pipe_bpp = 24;
                break;
@@ -5238,7 +5303,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
        } else if (is_sdvo && is_tv)
                factor = 20;
 
-       if (clock.m1 < factor * clock.n)
+       if (clock.m < factor * clock.n)
                fp |= FP_CB_TUNE;
 
        dpll = 0;
@@ -5516,6 +5581,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
        drm_vblank_post_modeset(dev, pipe);
 
+       intel_crtc->dpms_mode = DRM_MODE_DPMS_ON;
+
        return ret;
 }
 
@@ -7714,10 +7781,12 @@ static void gen6_init_clock_gating(struct drm_device *dev)
                   ILK_DPARB_CLK_GATE  |
                   ILK_DPFD_CLK_GATE);
 
-       for_each_pipe(pipe)
+       for_each_pipe(pipe) {
                I915_WRITE(DSPCNTR(pipe),
                           I915_READ(DSPCNTR(pipe)) |
                           DISPPLANE_TRICKLE_FEED_DISABLE);
+               intel_flush_display_plane(dev_priv, pipe);
+       }
 }
 
 static void ivybridge_init_clock_gating(struct drm_device *dev)
@@ -7734,10 +7803,12 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
 
        I915_WRITE(ILK_DSPCLK_GATE, IVB_VRHUNIT_CLK_GATE);
 
-       for_each_pipe(pipe)
+       for_each_pipe(pipe) {
                I915_WRITE(DSPCNTR(pipe),
                           I915_READ(DSPCNTR(pipe)) |
                           DISPPLANE_TRICKLE_FEED_DISABLE);
+               intel_flush_display_plane(dev_priv, pipe);
+       }
 }
 
 static void g4x_init_clock_gating(struct drm_device *dev)
@@ -7820,6 +7891,7 @@ static void ibx_init_clock_gating(struct drm_device *dev)
 static void cpt_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe;
 
        /*
         * On Ibex Peak and Cougar Point, we need to disable clock
@@ -7829,6 +7901,9 @@ static void cpt_init_clock_gating(struct drm_device *dev)
        I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE);
        I915_WRITE(SOUTH_CHICKEN2, I915_READ(SOUTH_CHICKEN2) |
                   DPLS_EDP_PPS_FIX_DIS);
+       /* Without this, mode sets may fail silently on FDI */
+       for_each_pipe(pipe)
+               I915_WRITE(TRANS_CHICKEN2(pipe), TRANS_AUTOTRAIN_GEN_STALL_DIS);
 }
 
 static void ironlake_teardown_rc6(struct drm_device *dev)
@@ -8178,6 +8253,9 @@ struct intel_quirk intel_quirks[] = {
 
        /* Lenovo U160 cannot use SSC on LVDS */
        { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable },
+
+       /* Sony Vaio Y cannot use SSC on LVDS */
+       { 0x0046, 0x104d, 0x9076, quirk_ssc_force_disable },
 };
 
 static void intel_init_quirks(struct drm_device *dev)
index f797fb58ba9c16e586ccb08927d7e71b95538149..0feae908bb37f7bc8cbc642ea12f96a6cabf467a 100644 (file)
@@ -50,9 +50,10 @@ struct intel_dp {
        bool has_audio;
        int force_audio;
        uint32_t color_range;
+       int dpms_mode;
        uint8_t link_bw;
        uint8_t lane_count;
-       uint8_t dpcd[4];
+       uint8_t dpcd[8];
        struct i2c_adapter adapter;
        struct i2c_algo_dp_aux_data algo;
        bool is_pch_edp;
@@ -316,9 +317,17 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
        else
                precharge = 5;
 
-       if (I915_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) {
-               DRM_ERROR("dp_aux_ch not started status 0x%08x\n",
-                         I915_READ(ch_ctl));
+       /* Try to wait for any previous AUX channel activity */
+       for (try = 0; try < 3; try++) {
+               status = I915_READ(ch_ctl);
+               if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
+                       break;
+               msleep(1);
+       }
+
+       if (try == 3) {
+               WARN(1, "dp_aux_ch not started status 0x%08x\n",
+                    I915_READ(ch_ctl));
                return -EBUSY;
        }
 
@@ -770,6 +779,7 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
        memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
        intel_dp->link_configuration[0] = intel_dp->link_bw;
        intel_dp->link_configuration[1] = intel_dp->lane_count;
+       intel_dp->link_configuration[8] = DP_SET_ANSI_8B10B;
 
        /*
         * Check for DPCD version > 1.1 and enhanced framing support
@@ -1011,6 +1021,8 @@ static void intel_dp_commit(struct drm_encoder *encoder)
 
        if (is_edp(intel_dp))
                ironlake_edp_backlight_on(dev);
+
+       intel_dp->dpms_mode = DRM_MODE_DPMS_ON;
 }
 
 static void
@@ -1045,6 +1057,7 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
                if (is_edp(intel_dp))
                        ironlake_edp_backlight_on(dev);
        }
+       intel_dp->dpms_mode = mode;
 }
 
 /*
@@ -1334,10 +1347,16 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
        u32 reg;
        uint32_t DP = intel_dp->DP;
 
-       /* Enable output, wait for it to become active */
-       I915_WRITE(intel_dp->output_reg, intel_dp->DP);
-       POSTING_READ(intel_dp->output_reg);
-       intel_wait_for_vblank(dev, intel_crtc->pipe);
+       /*
+        * On CPT we have to enable the port in training pattern 1, which
+        * will happen below in intel_dp_set_link_train.  Otherwise, enable
+        * the port and wait for it to become active.
+        */
+       if (!HAS_PCH_CPT(dev)) {
+               I915_WRITE(intel_dp->output_reg, intel_dp->DP);
+               POSTING_READ(intel_dp->output_reg);
+               intel_wait_for_vblank(dev, intel_crtc->pipe);
+       }
 
        /* Write the link configuration data */
        intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET,
@@ -1370,7 +1389,8 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
                        reg = DP | DP_LINK_TRAIN_PAT_1;
 
                if (!intel_dp_set_link_train(intel_dp, reg,
-                                            DP_TRAINING_PATTERN_1))
+                                            DP_TRAINING_PATTERN_1 |
+                                            DP_LINK_SCRAMBLING_DISABLE))
                        break;
                /* Set training pattern 1 */
 
@@ -1445,7 +1465,8 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
 
                /* channel eq pattern */
                if (!intel_dp_set_link_train(intel_dp, reg,
-                                            DP_TRAINING_PATTERN_2))
+                                            DP_TRAINING_PATTERN_2 |
+                                            DP_LINK_SCRAMBLING_DISABLE))
                        break;
 
                udelay(400);
@@ -1559,6 +1580,18 @@ intel_dp_link_down(struct intel_dp *intel_dp)
        POSTING_READ(intel_dp->output_reg);
 }
 
+static bool
+intel_dp_get_dpcd(struct intel_dp *intel_dp)
+{
+       if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd,
+                                          sizeof (intel_dp->dpcd)) &&
+           (intel_dp->dpcd[DP_DPCD_REV] != 0)) {
+               return true;
+       }
+
+       return false;
+}
+
 /*
  * According to DP spec
  * 5.1.2:
@@ -1571,36 +1604,44 @@ intel_dp_link_down(struct intel_dp *intel_dp)
 static void
 intel_dp_check_link_status(struct intel_dp *intel_dp)
 {
-       int ret;
+       if (intel_dp->dpms_mode != DRM_MODE_DPMS_ON)
+               return;
 
        if (!intel_dp->base.base.crtc)
                return;
 
+       /* Try to read receiver status if the link appears to be up */
        if (!intel_dp_get_link_status(intel_dp)) {
                intel_dp_link_down(intel_dp);
                return;
        }
 
-       /* Try to read receiver status if the link appears to be up */
-       ret = intel_dp_aux_native_read(intel_dp,
-                                      0x000, intel_dp->dpcd,
-                                      sizeof (intel_dp->dpcd));
-       if (ret != sizeof(intel_dp->dpcd)) {
+       /* Now read the DPCD to see if it's actually running */
+       if (!intel_dp_get_dpcd(intel_dp)) {
                intel_dp_link_down(intel_dp);
                return;
        }
 
        if (!intel_channel_eq_ok(intel_dp)) {
+               DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n",
+                             drm_get_encoder_name(&intel_dp->base.base));
                intel_dp_start_link_train(intel_dp);
                intel_dp_complete_link_train(intel_dp);
        }
 }
 
+static enum drm_connector_status
+intel_dp_detect_dpcd(struct intel_dp *intel_dp)
+{
+       if (intel_dp_get_dpcd(intel_dp))
+               return connector_status_connected;
+       return connector_status_disconnected;
+}
+
 static enum drm_connector_status
 ironlake_dp_detect(struct intel_dp *intel_dp)
 {
        enum drm_connector_status status;
-       bool ret;
 
        /* Can't disconnect eDP, but you can close the lid... */
        if (is_edp(intel_dp)) {
@@ -1610,15 +1651,7 @@ ironlake_dp_detect(struct intel_dp *intel_dp)
                return status;
        }
 
-       status = connector_status_disconnected;
-       ret = intel_dp_aux_native_read_retry(intel_dp,
-                                            0x000, intel_dp->dpcd,
-                                            sizeof (intel_dp->dpcd));
-       if (ret && intel_dp->dpcd[DP_DPCD_REV] != 0)
-               status = connector_status_connected;
-       DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx\n", intel_dp->dpcd[0],
-                     intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3]);
-       return status;
+       return intel_dp_detect_dpcd(intel_dp);
 }
 
 static enum drm_connector_status
@@ -1626,7 +1659,6 @@ g4x_dp_detect(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp->base.base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       enum drm_connector_status status;
        uint32_t temp, bit;
 
        switch (intel_dp->output_reg) {
@@ -1648,15 +1680,7 @@ g4x_dp_detect(struct intel_dp *intel_dp)
        if ((temp & bit) == 0)
                return connector_status_disconnected;
 
-       status = connector_status_disconnected;
-       if (intel_dp_aux_native_read(intel_dp, 0x000, intel_dp->dpcd,
-                                    sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd))
-       {
-               if (intel_dp->dpcd[DP_DPCD_REV] != 0)
-                       status = connector_status_connected;
-       }
-
-       return status;
+       return intel_dp_detect_dpcd(intel_dp);
 }
 
 /**
@@ -1679,6 +1703,12 @@ intel_dp_detect(struct drm_connector *connector, bool force)
                status = ironlake_dp_detect(intel_dp);
        else
                status = g4x_dp_detect(intel_dp);
+
+       DRM_DEBUG_KMS("DPCD: %02hx%02hx%02hx%02hx%02hx%02hx%02hx%02hx\n",
+                     intel_dp->dpcd[0], intel_dp->dpcd[1], intel_dp->dpcd[2],
+                     intel_dp->dpcd[3], intel_dp->dpcd[4], intel_dp->dpcd[5],
+                     intel_dp->dpcd[6], intel_dp->dpcd[7]);
+
        if (status != connector_status_connected)
                return status;
 
@@ -1924,6 +1954,7 @@ intel_dp_init(struct drm_device *dev, int output_reg)
                return;
 
        intel_dp->output_reg = output_reg;
+       intel_dp->dpms_mode = -1;
 
        intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
        if (!intel_connector) {
@@ -2000,7 +2031,7 @@ intel_dp_init(struct drm_device *dev, int output_reg)
 
        /* Cache some DPCD data in the eDP case */
        if (is_edp(intel_dp)) {
-               int ret;
+               bool ret;
                u32 pp_on, pp_div;
 
                pp_on = I915_READ(PCH_PP_ON_DELAYS);
@@ -2013,11 +2044,9 @@ intel_dp_init(struct drm_device *dev, int output_reg)
                dev_priv->panel_t12 *= 100; /* t12 in 100ms units */
 
                ironlake_edp_panel_vdd_on(intel_dp);
-               ret = intel_dp_aux_native_read(intel_dp, DP_DPCD_REV,
-                                              intel_dp->dpcd,
-                                              sizeof(intel_dp->dpcd));
+               ret = intel_dp_get_dpcd(intel_dp);
                ironlake_edp_panel_vdd_off(intel_dp);
-               if (ret == sizeof(intel_dp->dpcd)) {
+               if (ret) {
                        if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11)
                                dev_priv->no_aux_handshake =
                                        intel_dp->dpcd[DP_MAX_DOWNSPREAD] &
index 6e990f9760ef17b49bb183e6853e8909ec96fb08..7b330e76a435afa32ba126d7a7837bb816ceb589 100644 (file)
@@ -178,10 +178,28 @@ struct intel_crtc {
 #define to_intel_encoder(x) container_of(x, struct intel_encoder, base)
 #define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base)
 
+#define DIP_HEADER_SIZE        5
+
 #define DIP_TYPE_AVI    0x82
 #define DIP_VERSION_AVI 0x2
 #define DIP_LEN_AVI     13
 
+#define DIP_TYPE_SPD   0x3
+#define DIP_VERSION_SPD        0x1
+#define DIP_LEN_SPD    25
+#define DIP_SPD_UNKNOWN        0
+#define DIP_SPD_DSTB   0x1
+#define DIP_SPD_DVDP   0x2
+#define DIP_SPD_DVHS   0x3
+#define DIP_SPD_HDDVR  0x4
+#define DIP_SPD_DVC    0x5
+#define DIP_SPD_DSC    0x6
+#define DIP_SPD_VCD    0x7
+#define DIP_SPD_GAME   0x8
+#define DIP_SPD_PC     0x9
+#define DIP_SPD_BD     0xa
+#define DIP_SPD_SCD    0xb
+
 struct dip_infoframe {
        uint8_t type;           /* HB0 */
        uint8_t ver;            /* HB1 */
@@ -206,6 +224,11 @@ struct dip_infoframe {
                        uint16_t left_bar_end;
                        uint16_t right_bar_start;
                } avi;
+               struct {
+                       uint8_t vn[8];
+                       uint8_t pd[16];
+                       uint8_t sdi;
+               } spd;
                uint8_t payload[27];
        } __attribute__ ((packed)) body;
 } __attribute__((packed));
index 1ed8e6903915ff542e79ef2cd8becf89b3ba8912..226ba830f3837c10699287c207686eb16df19611 100644 (file)
@@ -45,6 +45,8 @@ struct intel_hdmi {
        bool has_hdmi_sink;
        bool has_audio;
        int force_audio;
+       void (*write_infoframe)(struct drm_encoder *encoder,
+                               struct dip_infoframe *frame);
 };
 
 static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)
@@ -58,37 +60,70 @@ static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector)
                            struct intel_hdmi, base);
 }
 
-void intel_dip_infoframe_csum(struct dip_infoframe *avi_if)
+void intel_dip_infoframe_csum(struct dip_infoframe *frame)
 {
-       uint8_t *data = (uint8_t *)avi_if;
+       uint8_t *data = (uint8_t *)frame;
        uint8_t sum = 0;
        unsigned i;
 
-       avi_if->checksum = 0;
-       avi_if->ecc = 0;
+       frame->checksum = 0;
+       frame->ecc = 0;
 
-       for (i = 0; i < sizeof(*avi_if); i++)
+       /* Header isn't part of the checksum */
+       for (i = 5; i < frame->len; i++)
                sum += data[i];
 
-       avi_if->checksum = 0x100 - sum;
+       frame->checksum = 0x100 - sum;
 }
 
-static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+static u32 intel_infoframe_index(struct dip_infoframe *frame)
 {
-       struct dip_infoframe avi_if = {
-               .type = DIP_TYPE_AVI,
-               .ver = DIP_VERSION_AVI,
-               .len = DIP_LEN_AVI,
-       };
-       uint32_t *data = (uint32_t *)&avi_if;
+       u32 flags = 0;
+
+       switch (frame->type) {
+       case DIP_TYPE_AVI:
+               flags |= VIDEO_DIP_SELECT_AVI;
+               break;
+       case DIP_TYPE_SPD:
+               flags |= VIDEO_DIP_SELECT_SPD;
+               break;
+       default:
+               DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
+               break;
+       }
+
+       return flags;
+}
+
+static u32 intel_infoframe_flags(struct dip_infoframe *frame)
+{
+       u32 flags = 0;
+
+       switch (frame->type) {
+       case DIP_TYPE_AVI:
+               flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC;
+               break;
+       case DIP_TYPE_SPD:
+               flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_2VSYNC;
+               break;
+       default:
+               DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
+               break;
+       }
+
+       return flags;
+}
+
+static void i9xx_write_infoframe(struct drm_encoder *encoder,
+                                struct dip_infoframe *frame)
+{
+       uint32_t *data = (uint32_t *)frame;
        struct drm_device *dev = encoder->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
-       u32 port;
-       unsigned i;
+       u32 port, flags, val = I915_READ(VIDEO_DIP_CTL);
+       unsigned i, len = DIP_HEADER_SIZE + frame->len;
 
-       if (!intel_hdmi->has_hdmi_sink)
-               return;
 
        /* XXX first guess at handling video port, is this corrent? */
        if (intel_hdmi->sdvox_reg == SDVOB)
@@ -98,18 +133,87 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
        else
                return;
 
-       I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |
-                  VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC);
+       flags = intel_infoframe_index(frame);
+
+       val &= ~VIDEO_DIP_SELECT_MASK;
 
-       intel_dip_infoframe_csum(&avi_if);
-       for (i = 0; i < sizeof(avi_if); i += 4) {
+       I915_WRITE(VIDEO_DIP_CTL, val | port | flags);
+
+       for (i = 0; i < len; i += 4) {
                I915_WRITE(VIDEO_DIP_DATA, *data);
                data++;
        }
 
-       I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |
-                  VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC |
-                  VIDEO_DIP_ENABLE_AVI);
+       flags |= intel_infoframe_flags(frame);
+
+       I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags);
+}
+
+static void ironlake_write_infoframe(struct drm_encoder *encoder,
+                                    struct dip_infoframe *frame)
+{
+       uint32_t *data = (uint32_t *)frame;
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_crtc *crtc = encoder->crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
+       unsigned i, len = DIP_HEADER_SIZE + frame->len;
+       u32 flags, val = I915_READ(reg);
+
+       intel_wait_for_vblank(dev, intel_crtc->pipe);
+
+       flags = intel_infoframe_index(frame);
+
+       val &= ~VIDEO_DIP_SELECT_MASK;
+
+       I915_WRITE(reg, val | flags);
+
+       for (i = 0; i < len; i += 4) {
+               I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
+               data++;
+       }
+
+       flags |= intel_infoframe_flags(frame);
+
+       I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags);
+}
+static void intel_set_infoframe(struct drm_encoder *encoder,
+                               struct dip_infoframe *frame)
+{
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+
+       if (!intel_hdmi->has_hdmi_sink)
+               return;
+
+       intel_dip_infoframe_csum(frame);
+       intel_hdmi->write_infoframe(encoder, frame);
+}
+
+static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+{
+       struct dip_infoframe avi_if = {
+               .type = DIP_TYPE_AVI,
+               .ver = DIP_VERSION_AVI,
+               .len = DIP_LEN_AVI,
+       };
+
+       intel_set_infoframe(encoder, &avi_if);
+}
+
+static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
+{
+       struct dip_infoframe spd_if;
+
+       memset(&spd_if, 0, sizeof(spd_if));
+       spd_if.type = DIP_TYPE_SPD;
+       spd_if.ver = DIP_VERSION_SPD;
+       spd_if.len = DIP_LEN_SPD;
+       strcpy(spd_if.body.spd.vn, "Intel");
+       strcpy(spd_if.body.spd.pd, "Integrated gfx");
+       spd_if.body.spd.sdi = DIP_SPD_PC;
+
+       intel_set_infoframe(encoder, &spd_if);
 }
 
 static void intel_hdmi_mode_set(struct drm_encoder *encoder,
@@ -156,6 +260,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
        POSTING_READ(intel_hdmi->sdvox_reg);
 
        intel_hdmi_set_avi_infoframe(encoder);
+       intel_hdmi_set_spd_infoframe(encoder);
 }
 
 static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
@@ -433,6 +538,11 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
 
        intel_hdmi->sdvox_reg = sdvox_reg;
 
+       if (!HAS_PCH_SPLIT(dev))
+               intel_hdmi->write_infoframe = i9xx_write_infoframe;
+       else
+               intel_hdmi->write_infoframe = ironlake_write_infoframe;
+
        drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);
 
        intel_hdmi_add_properties(intel_hdmi, connector);
index b28f7bd9f88a1fca15fdb0ade73cb724658d1cf8..2e8ddfcba40c8c7d751776fb4d0b6818189b9c7e 100644 (file)
@@ -688,6 +688,14 @@ static const struct dmi_system_id intel_no_lvds[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Studio Hybrid 140g"),
                },
        },
+       {
+               .callback = intel_no_lvds_dmi_callback,
+               .ident = "Dell OptiPlex FX170",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex FX170"),
+               },
+       },
        {
                .callback = intel_no_lvds_dmi_callback,
                .ident = "AOpen Mini PC",
index a06ff07a4d3b0791ff23319903898817fc722f34..05f500cd9c246c0156412e028f5bef0c35acd22b 100644 (file)
@@ -83,11 +83,15 @@ intel_pch_panel_fitting(struct drm_device *dev,
                        u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
                        if (scaled_width > scaled_height) { /* pillar */
                                width = scaled_height / mode->vdisplay;
+                               if (width & 1)
+                                       width++;
                                x = (adjusted_mode->hdisplay - width + 1) / 2;
                                y = 0;
                                height = adjusted_mode->vdisplay;
                        } else if (scaled_width < scaled_height) { /* letter */
                                height = scaled_width / mode->hdisplay;
+                               if (height & 1)
+                                   height++;
                                y = (adjusted_mode->vdisplay - height + 1) / 2;
                                x = 0;
                                width = adjusted_mode->hdisplay;
index e9615685a39cd771239e2a0d087b7128327d9575..47b9b277703856e6b1f3d76d81ce70be25f3d296 100644 (file)
@@ -1321,6 +1321,9 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size)
                ring->get_seqno = pc_render_get_seqno;
        }
 
+       if (!I915_NEED_GFX_HWS(dev))
+               ring->status_page.page_addr = dev_priv->status_page_dmah->vaddr;
+
        ring->dev = dev;
        INIT_LIST_HEAD(&ring->active_list);
        INIT_LIST_HEAD(&ring->request_list);
index 3896ef8111026bdfa2a391212f42c576c8a4a404..9f363e0c4b6082561295eb5f5e8a78559e42dccd 100644 (file)
@@ -5,6 +5,7 @@
 ccflags-y := -Iinclude/drm
 
 hostprogs-y := mkregtable
+clean-files := rn50_reg_safe.h r100_reg_safe.h r200_reg_safe.h rv515_reg_safe.h r300_reg_safe.h r420_reg_safe.h rs600_reg_safe.h r600_reg_safe.h evergreen_reg_safe.h cayman_reg_safe.h
 
 quiet_cmd_mkregtable = MKREGTABLE $@
       cmd_mkregtable = $(obj)/mkregtable $< > $@
index ebdb0fdb8348ad7b217e3d46027207789e0ad67f..e88c64417a8a74feac92ec9e7e763426f96045ff 100644 (file)
@@ -1245,6 +1245,9 @@ struct atom_context *atom_parse(struct card_info *card, void *bios)
        char name[512];
        int i;
 
+       if (!ctx)
+               return NULL;
+
        ctx->card = card;
        ctx->bios = bios;
 
index 189e86522b5b9654bae3d565f35d578f7ba0a1dd..a134790903d3b6354a74f13cee1e7c62c8807cda 100644 (file)
@@ -428,7 +428,7 @@ static inline int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u3
                last_reg = ARRAY_SIZE(evergreen_reg_safe_bm);
 
        i = (reg >> 7);
-       if (i > last_reg) {
+       if (i >= last_reg) {
                dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
                return -EINVAL;
        }
index db8ef1905d5f500f6792e7f3dfd11e5115c2b9df..cf83aa05a6845e1cc62a9a6da43a17ba75cb588a 100644 (file)
@@ -915,12 +915,11 @@ 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) {
+       if (i >= ARRAY_SIZE(r600_reg_safe_bm)) {
                dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
                return -EINVAL;
        }
index a74217cd192fed2facc49f8ae65f2367dff212d4..e0138b674aca038e99b020000cdfec0285aef405 100644 (file)
@@ -2557,6 +2557,7 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev)
        u16 offset, misc, misc2 = 0;
        u8 rev, blocks, tmp;
        int state_index = 0;
+       struct radeon_i2c_bus_rec i2c_bus;
 
        rdev->pm.default_power_state_index = -1;
 
@@ -2575,7 +2576,6 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev)
        offset = combios_get_table_offset(dev, COMBIOS_OVERDRIVE_INFO_TABLE);
        if (offset) {
                u8 thermal_controller = 0, gpio = 0, i2c_addr = 0, clk_bit = 0, data_bit = 0;
-               struct radeon_i2c_bus_rec i2c_bus;
 
                rev = RBIOS8(offset);
 
@@ -2617,6 +2617,25 @@ void radeon_combios_get_power_modes(struct radeon_device *rdev)
                                i2c_new_device(&rdev->pm.i2c_bus->adapter, &info);
                        }
                }
+       } else {
+               /* boards with a thermal chip, but no overdrive table */
+
+               /* Asus 9600xt has an f75375 on the monid bus */
+               if ((dev->pdev->device == 0x4152) &&
+                   (dev->pdev->subsystem_vendor == 0x1043) &&
+                   (dev->pdev->subsystem_device == 0xc002)) {
+                       i2c_bus = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0);
+                       rdev->pm.i2c_bus = radeon_i2c_lookup(rdev, &i2c_bus);
+                       if (rdev->pm.i2c_bus) {
+                               struct i2c_board_info info = { };
+                               const char *name = "f75375";
+                               info.addr = 0x28;
+                               strlcpy(info.type, name, sizeof(info.type));
+                               i2c_new_device(&rdev->pm.i2c_bus->adapter, &info);
+                               DRM_INFO("Possible %s thermal controller at 0x%02x\n",
+                                        name, info.addr);
+                       }
+               }
        }
 
        if (rdev->flags & RADEON_IS_MOBILITY) {
index 9792d4ffdc86250e102457c5bd36765098dde7e3..6d6b5f16bc09461af70ea4eefcd1046999b113aa 100644 (file)
@@ -430,6 +430,45 @@ int radeon_connector_set_property(struct drm_connector *connector, struct drm_pr
        return 0;
 }
 
+/*
+ * Some integrated ATI Radeon chipset implementations (e. g.
+ * Asus M2A-VM HDMI) may indicate the availability of a DDC,
+ * even when there's no monitor connected. For these connectors
+ * following DDC probe extension will be applied: check also for the
+ * availability of EDID with at least a correct EDID header. Only then,
+ * DDC is assumed to be available. This prevents drm_get_edid() and
+ * drm_edid_block_valid() from periodically dumping data and kernel
+ * errors into the logs and onto the terminal.
+ */
+static bool radeon_connector_needs_extended_probe(struct radeon_device *dev,
+                                    uint32_t supported_device,
+                                    int connector_type)
+{
+       /* Asus M2A-VM HDMI board sends data to i2c bus even,
+        * if HDMI add-on card is not plugged in or HDMI is disabled in
+        * BIOS. Valid DDC can only be assumed, if also a valid EDID header
+        * can be retrieved via i2c bus during DDC probe */
+       if ((dev->pdev->device == 0x791e) &&
+           (dev->pdev->subsystem_vendor == 0x1043) &&
+           (dev->pdev->subsystem_device == 0x826d)) {
+               if ((connector_type == DRM_MODE_CONNECTOR_HDMIA) &&
+                   (supported_device == ATOM_DEVICE_DFP2_SUPPORT))
+                       return true;
+       }
+       /* ECS A740GM-M with ATI RADEON 2100 sends data to i2c bus
+        * for a DVI connector that is not implemented */
+       if ((dev->pdev->device == 0x796e) &&
+           (dev->pdev->subsystem_vendor == 0x1019) &&
+           (dev->pdev->subsystem_device == 0x2615)) {
+               if ((connector_type == DRM_MODE_CONNECTOR_DVID) &&
+                   (supported_device == ATOM_DEVICE_DFP2_SUPPORT))
+                       return true;
+       }
+
+       /* Default: no EDID header probe required for DDC probing */
+       return false;
+}
+
 static void radeon_fixup_lvds_native_mode(struct drm_encoder *encoder,
                                          struct drm_connector *connector)
 {
@@ -661,7 +700,8 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
                ret = connector_status_disconnected;
 
        if (radeon_connector->ddc_bus)
-               dret = radeon_ddc_probe(radeon_connector);
+               dret = radeon_ddc_probe(radeon_connector,
+                                       radeon_connector->requires_extended_probe);
        if (dret) {
                if (radeon_connector->edid) {
                        kfree(radeon_connector->edid);
@@ -833,7 +873,8 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
        bool dret = false;
 
        if (radeon_connector->ddc_bus)
-               dret = radeon_ddc_probe(radeon_connector);
+               dret = radeon_ddc_probe(radeon_connector,
+                                       radeon_connector->requires_extended_probe);
        if (dret) {
                if (radeon_connector->edid) {
                        kfree(radeon_connector->edid);
@@ -1251,7 +1292,8 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
                                if (radeon_dp_getdpcd(radeon_connector))
                                        ret = connector_status_connected;
                        } else {
-                               if (radeon_ddc_probe(radeon_connector))
+                               if (radeon_ddc_probe(radeon_connector,
+                                                    radeon_connector->requires_extended_probe))
                                        ret = connector_status_connected;
                        }
                }
@@ -1406,6 +1448,9 @@ radeon_add_atom_connector(struct drm_device *dev,
        radeon_connector->shared_ddc = shared_ddc;
        radeon_connector->connector_object_id = connector_object_id;
        radeon_connector->hpd = *hpd;
+       radeon_connector->requires_extended_probe =
+               radeon_connector_needs_extended_probe(rdev, supported_device,
+                                                       connector_type);
        radeon_connector->router = *router;
        if (router->ddc_valid || router->cd_valid) {
                radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info);
@@ -1752,6 +1797,9 @@ radeon_add_legacy_connector(struct drm_device *dev,
        radeon_connector->devices = supported_device;
        radeon_connector->connector_object_id = connector_object_id;
        radeon_connector->hpd = *hpd;
+       radeon_connector->requires_extended_probe =
+               radeon_connector_needs_extended_probe(rdev, supported_device,
+                                                       connector_type);
        switch (connector_type) {
        case DRM_MODE_CONNECTOR_VGA:
                drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
index 7cfaa7e2f3b57195671beb6958c8447f9f108683..440e6ecccc40054c5620e38db033da76ee3dd3aa 100644 (file)
@@ -704,8 +704,9 @@ int radeon_device_init(struct radeon_device *rdev,
        rdev->gpu_lockup = false;
        rdev->accel_working = false;
 
-       DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X).\n",
-               radeon_family_name[rdev->family], pdev->vendor, pdev->device);
+       DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X 0x%04X:0x%04X).\n",
+               radeon_family_name[rdev->family], pdev->vendor, pdev->device,
+               pdev->subsystem_vendor, pdev->subsystem_device);
 
        /* mutex initialization are all done here so we
         * can recall function without having locking issues */
index 28f4655905bc50d52151aeb190ab9ce7a28fb3db..1a858944e4f3f31243ec55d5178ea7cd7b1f3422 100644 (file)
@@ -751,8 +751,17 @@ static int radeon_ddc_dump(struct drm_connector *connector)
        if (!radeon_connector->ddc_bus)
                return -1;
        edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter);
+       /* Log EDID retrieval status here. In particular with regard to
+        * connectors with requires_extended_probe flag set, that will prevent
+        * function radeon_dvi_detect() to fetch EDID on this connector,
+        * as long as there is no valid EDID header found */
        if (edid) {
+               DRM_INFO("Radeon display connector %s: Found valid EDID",
+                               drm_get_connector_name(connector));
                kfree(edid);
+       } else {
+               DRM_INFO("Radeon display connector %s: No monitor connected or invalid EDID",
+                               drm_get_connector_name(connector));
        }
        return ret;
 }
index 85f033f19a8ad76d74f0c90bcf48c93335c22c05..e71d2ed7fa113126e7cacb06b1553f8767b948e6 100644 (file)
@@ -50,8 +50,8 @@
  *   2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add eg dyn gpr regs
  *   2.8.0 - pageflip support, r500 US_FORMAT regs. r500 ARGB2101010 colorbuf, r300->r500 CMASK, clock crystal query
  *   2.9.0 - r600 tiling (s3tc,rgtc) working, SET_PREDICATION packet 3 on r600 + eg, backend query
- *   2.10.0 - fusion 2D tiling, initial compute support for the CS checker
- *   2.11.0 - backend map
+ *   2.10.0 - fusion 2D tiling
+ *   2.11.0 - backend map, initial compute support for the CS checker
  */
 #define KMS_DRIVER_MAJOR       2
 #define KMS_DRIVER_MINOR       11
index 781196db792f15ff919df30323cf37c5aca4ab63..6c111c1fa3f9c467f27d0a220373cf1777d80b5e 100644 (file)
  * radeon_ddc_probe
  *
  */
-bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
+bool radeon_ddc_probe(struct radeon_connector *radeon_connector, bool requires_extended_probe)
 {
-       u8 out_buf[] = { 0x0, 0x0};
-       u8 buf[2];
+       u8 out = 0x0;
+       u8 buf[8];
        int ret;
        struct i2c_msg msgs[] = {
                {
                        .addr = 0x50,
                        .flags = 0,
                        .len = 1,
-                       .buf = out_buf,
+                       .buf = &out,
                },
                {
                        .addr = 0x50,
@@ -52,15 +52,31 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
                }
        };
 
+       /* Read 8 bytes from i2c for extended probe of EDID header */
+       if (requires_extended_probe)
+               msgs[1].len = 8;
+
        /* on hw with routers, select right port */
        if (radeon_connector->router.ddc_valid)
                radeon_router_select_ddc_port(radeon_connector);
 
        ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2);
-       if (ret == 2)
-               return true;
-
-       return false;
+       if (ret != 2)
+               /* Couldn't find an accessible DDC on this connector */
+               return false;
+       if (requires_extended_probe) {
+               /* Probe also for valid EDID header
+                * EDID header starts with:
+                * 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00.
+                * Only the first 6 bytes must be valid as
+                * drm_edid_block_valid() can fix the last 2 bytes */
+               if (drm_edid_header_is_valid(buf) < 6) {
+                       /* Couldn't find an accessible EDID on this
+                        * connector */
+                       return false;
+               }
+       }
+       return true;
 }
 
 /* bit banging i2c */
index 6df4e3cec0c23cdced34b0da862eddd6d08c2c88..d09031c03e262606677f5abb1d59576066b1f695 100644 (file)
@@ -438,6 +438,9 @@ struct radeon_connector {
        struct radeon_i2c_chan *ddc_bus;
        /* some systems have an hdmi and vga port with a shared ddc line */
        bool shared_ddc;
+       /* for some Radeon chip families we apply an additional EDID header
+          check as part of the DDC probe */
+       bool requires_extended_probe;
        bool use_digital;
        /* we need to mind the EDID between detect
           and get modes due to analog/digital/tvencoder */
@@ -514,7 +517,8 @@ extern void radeon_i2c_put_byte(struct radeon_i2c_chan *i2c,
                                u8 val);
 extern void radeon_router_select_ddc_port(struct radeon_connector *radeon_connector);
 extern void radeon_router_select_cd_port(struct radeon_connector *radeon_connector);
-extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector);
+extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector,
+                       bool requires_extended_probe);
 extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector);
 
 extern struct drm_encoder *radeon_best_encoder(struct drm_connector *connector);
index 3be60da5212376358f2ac6c8a6181c63e3601dc7..67cbcfa351225b4f7c60dce041562a411d76430e 100644 (file)
@@ -141,6 +141,8 @@ static void cy82c693_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
                pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, time_16);
                pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, time_8);
        }
+       if (hwif->index > 0)
+               pci_dev_put(dev);
 }
 
 static void __devinit init_iops_cy82c693(ide_hwif_t *hwif)
index 542603b394e4a59997ca82940598b3a64f8edfd9..962693b10a1cfc8dc7aa71a8439329cc79f946f3 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/ata_platform.h>
 #include <linux/platform_device.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 
 static void __devinit plat_ide_setup_ports(struct ide_hw *hw,
@@ -95,7 +96,10 @@ static int __devinit plat_ide_probe(struct platform_device *pdev)
        plat_ide_setup_ports(&hw, base, alt_base, pdata, res_irq->start);
        hw.dev = &pdev->dev;
 
-       d.irq_flags = res_irq->flags;
+       d.irq_flags = res_irq->flags & IRQF_TRIGGER_MASK;
+       if (res_irq->flags & IORESOURCE_IRQ_SHAREABLE)
+               d.irq_flags |= IRQF_SHARED;
+
        if (mmio)
                d.host_flags |= IDE_HFLAG_MMIO;
 
index ce281d152275783a8c27893d177b41fb9654a2a2..67df91af84244c40185ecb27c4bbdd2ae29f615b 100644 (file)
@@ -483,7 +483,7 @@ static int gpio_keys_get_devtree_pdata(struct device *dev,
 
        buttons = kzalloc(pdata->nbuttons * (sizeof *buttons), GFP_KERNEL);
        if (!buttons)
-               return -ENODEV;
+               return -ENOMEM;
 
        pp = NULL;
        i = 0;
index ab0acaf7fe8fdbc26ddb42a906db9f832826145d..756348a7f93aef4841427b4f6d8ae0165f9603b8 100644 (file)
@@ -754,8 +754,11 @@ fail3:
        device_remove_file(&client->dev, &dev_attr_disable_kp);
 fail2:
        while (--pwm >= 0)
-               if (lm->pwm[pwm].enabled)
+               if (lm->pwm[pwm].enabled) {
+                       device_remove_file(lm->pwm[pwm].cdev.dev,
+                                          &dev_attr_time);
                        led_classdev_unregister(&lm->pwm[pwm].cdev);
+               }
 fail1:
        input_free_device(idev);
        kfree(lm);
@@ -775,8 +778,10 @@ static int __devexit lm8323_remove(struct i2c_client *client)
        device_remove_file(&lm->client->dev, &dev_attr_disable_kp);
 
        for (i = 0; i < 3; i++)
-               if (lm->pwm[i].enabled)
+               if (lm->pwm[i].enabled) {
+                       device_remove_file(lm->pwm[i].cdev.dev, &dev_attr_time);
                        led_classdev_unregister(&lm->pwm[i].cdev);
+               }
 
        kfree(lm);
 
index da3828fc2c09fc151b129ebf4086cad8ecdf9bc3..f270447ba9519cb9497eea8a02d9869eef58ba4b 100644 (file)
@@ -19,6 +19,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
+#include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/input.h>
 #include <linux/platform_device.h>
@@ -37,7 +38,7 @@
 #define KBC_ROW_SCAN_DLY       5
 
 /* KBC uses a 32KHz clock so a cycle = 1/32Khz */
-#define KBC_CYCLE_USEC 32
+#define KBC_CYCLE_MS   32
 
 /* KBC Registers */
 
@@ -647,7 +648,7 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev)
        debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT);
        scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows;
        kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt;
-       kbc->repoll_dly = ((kbc->repoll_dly * KBC_CYCLE_USEC) + 999) / 1000;
+       kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS);
 
        input_dev->name = pdev->name;
        input_dev->id.bustype = BUS_HOST;
index c456f63b6bae0ab2167f6e197ba0d1f210a1fecd..783597a9a64a856d5f774172b3cde8fc5b1e32f8 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
+#include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/input/kxtj9.h>
 #include <linux/input-polldev.h>
index 20f8f9284f028e7f125224187d40db2ef7ed330d..6c76cf79299143ce16542af8e734e49e4a21bf12 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/input-polldev.h>
+#include <linux/of_device.h>
 
 #define MMA8450_DRV_NAME       "mma8450"
 
@@ -229,10 +230,17 @@ static const struct i2c_device_id mma8450_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, mma8450_id);
 
+static const struct of_device_id mma8450_dt_ids[] = {
+       { .compatible = "fsl,mma8450", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, mma8450_dt_ids);
+
 static struct i2c_driver mma8450_driver = {
        .driver = {
                .name   = MMA8450_DRV_NAME,
                .owner  = THIS_MODULE,
+               .of_match_table = mma8450_dt_ids,
        },
        .probe          = mma8450_probe,
        .remove         = __devexit_p(mma8450_remove),
index 95577c15ae56b68f832381f29f158dd7d81d34eb..4d17d9f3320ba821c1350c9d74bed595105ae4e0 100644 (file)
@@ -32,6 +32,7 @@
 #define DEBUG
 #include <linux/slab.h>
 #include <linux/input.h>
+#include <linux/module.h>
 #include <linux/serio.h>
 #include <linux/libps2.h>
 #include <linux/delay.h>
index 80baa53da5b169dd0d9f071b1009fdf8906d08fa..d64c5a43aaad238671f3958e9d0bd6e08da5010f 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/init.h>
 #include <linux/list.h>
 #include <linux/io.h>
-
+#include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/of_platform.h>
 
index bc3b5187f3a391504f275567d7143e878dda5c9b..131f9d1c921b090ee813e5bc2205fa5a904a0633 100644 (file)
@@ -249,12 +249,14 @@ static void __ad7879_enable(struct ad7879 *ts)
 
 static void __ad7879_disable(struct ad7879 *ts)
 {
+       u16 reg = (ts->cmd_crtl2 & ~AD7879_PM(-1)) |
+               AD7879_PM(AD7879_PM_SHUTDOWN);
        disable_irq(ts->irq);
 
        if (del_timer_sync(&ts->timer))
                ad7879_ts_event_release(ts);
 
-       ad7879_write(ts, AD7879_REG_CTRL2, AD7879_PM(AD7879_PM_SHUTDOWN));
+       ad7879_write(ts, AD7879_REG_CTRL2, reg);
 }
 
 
index 8420129fc5eed67693b99812ab565130b8ad1db7..f75a66e7d312a8e1efed3d2db37ba1c554785e94 100644 (file)
@@ -241,12 +241,13 @@ config DM_MIRROR
          needed for live data migration tools such as 'pvmove'.
 
 config DM_RAID
-       tristate "RAID 4/5/6 target (EXPERIMENTAL)"
+       tristate "RAID 1/4/5/6 target (EXPERIMENTAL)"
        depends on BLK_DEV_DM && EXPERIMENTAL
+       select MD_RAID1
        select MD_RAID456
        select BLK_DEV_MD
        ---help---
-        A dm target that supports RAID4, RAID5 and RAID6 mappings
+        A dm target that supports RAID1, RAID4, RAID5 and RAID6 mappings
 
         A RAID-5 set of N drives with a capacity of C MB per drive provides
         the capacity of C * (N - 1) MB, and protects against a failure
index bae6c4e23d3f7d02798478993c50b976767a81ee..49da55c1528aa01137a61c98d6033c9b84dc2e05 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/device-mapper.h>
 
 #define DM_MSG_PREFIX "crypt"
-#define MESG_STR(x) x, sizeof(x)
 
 /*
  * context holding the current state of a multi-part conversion
@@ -239,7 +238,7 @@ static int crypt_iv_plain_gen(struct crypt_config *cc, u8 *iv,
                              struct dm_crypt_request *dmreq)
 {
        memset(iv, 0, cc->iv_size);
-       *(u32 *)iv = cpu_to_le32(dmreq->iv_sector & 0xffffffff);
+       *(__le32 *)iv = cpu_to_le32(dmreq->iv_sector & 0xffffffff);
 
        return 0;
 }
@@ -248,7 +247,7 @@ static int crypt_iv_plain64_gen(struct crypt_config *cc, u8 *iv,
                                struct dm_crypt_request *dmreq)
 {
        memset(iv, 0, cc->iv_size);
-       *(u64 *)iv = cpu_to_le64(dmreq->iv_sector);
+       *(__le64 *)iv = cpu_to_le64(dmreq->iv_sector);
 
        return 0;
 }
@@ -415,7 +414,7 @@ static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv,
        struct crypto_cipher *essiv_tfm = this_crypt_config(cc)->iv_private;
 
        memset(iv, 0, cc->iv_size);
-       *(u64 *)iv = cpu_to_le64(dmreq->iv_sector);
+       *(__le64 *)iv = cpu_to_le64(dmreq->iv_sector);
        crypto_cipher_encrypt_one(essiv_tfm, iv, iv);
 
        return 0;
@@ -1575,11 +1574,17 @@ bad_mem:
 static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 {
        struct crypt_config *cc;
-       unsigned int key_size;
+       unsigned int key_size, opt_params;
        unsigned long long tmpll;
        int ret;
+       struct dm_arg_set as;
+       const char *opt_string;
+
+       static struct dm_arg _args[] = {
+               {0, 1, "Invalid number of feature args"},
+       };
 
-       if (argc != 5) {
+       if (argc < 5) {
                ti->error = "Not enough arguments";
                return -EINVAL;
        }
@@ -1648,6 +1653,30 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        }
        cc->start = tmpll;
 
+       argv += 5;
+       argc -= 5;
+
+       /* Optional parameters */
+       if (argc) {
+               as.argc = argc;
+               as.argv = argv;
+
+               ret = dm_read_arg_group(_args, &as, &opt_params, &ti->error);
+               if (ret)
+                       goto bad;
+
+               opt_string = dm_shift_arg(&as);
+
+               if (opt_params == 1 && opt_string &&
+                   !strcasecmp(opt_string, "allow_discards"))
+                       ti->num_discard_requests = 1;
+               else if (opt_params) {
+                       ret = -EINVAL;
+                       ti->error = "Invalid feature arguments";
+                       goto bad;
+               }
+       }
+
        ret = -ENOMEM;
        cc->io_queue = alloc_workqueue("kcryptd_io",
                                       WQ_NON_REENTRANT|
@@ -1682,9 +1711,16 @@ static int crypt_map(struct dm_target *ti, struct bio *bio,
        struct dm_crypt_io *io;
        struct crypt_config *cc;
 
-       if (bio->bi_rw & REQ_FLUSH) {
+       /*
+        * If bio is REQ_FLUSH or REQ_DISCARD, just bypass crypt queues.
+        * - for REQ_FLUSH device-mapper core ensures that no IO is in-flight
+        * - for REQ_DISCARD caller must use flush if IO ordering matters
+        */
+       if (unlikely(bio->bi_rw & (REQ_FLUSH | REQ_DISCARD))) {
                cc = ti->private;
                bio->bi_bdev = cc->dev->bdev;
+               if (bio_sectors(bio))
+                       bio->bi_sector = cc->start + dm_target_offset(ti, bio->bi_sector);
                return DM_MAPIO_REMAPPED;
        }
 
@@ -1727,6 +1763,10 @@ static int crypt_status(struct dm_target *ti, status_type_t type,
 
                DMEMIT(" %llu %s %llu", (unsigned long long)cc->iv_offset,
                                cc->dev->name, (unsigned long long)cc->start);
+
+               if (ti->num_discard_requests)
+                       DMEMIT(" 1 allow_discards");
+
                break;
        }
        return 0;
@@ -1770,12 +1810,12 @@ static int crypt_message(struct dm_target *ti, unsigned argc, char **argv)
        if (argc < 2)
                goto error;
 
-       if (!strnicmp(argv[0], MESG_STR("key"))) {
+       if (!strcasecmp(argv[0], "key")) {
                if (!test_bit(DM_CRYPT_SUSPENDED, &cc->flags)) {
                        DMWARN("not suspended during key manipulation.");
                        return -EINVAL;
                }
-               if (argc == 3 && !strnicmp(argv[1], MESG_STR("set"))) {
+               if (argc == 3 && !strcasecmp(argv[1], "set")) {
                        ret = crypt_set_key(cc, argv[2]);
                        if (ret)
                                return ret;
@@ -1783,7 +1823,7 @@ static int crypt_message(struct dm_target *ti, unsigned argc, char **argv)
                                ret = cc->iv_gen_ops->init(cc);
                        return ret;
                }
-               if (argc == 2 && !strnicmp(argv[1], MESG_STR("wipe"))) {
+               if (argc == 2 && !strcasecmp(argv[1], "wipe")) {
                        if (cc->iv_gen_ops && cc->iv_gen_ops->wipe) {
                                ret = cc->iv_gen_ops->wipe(cc);
                                if (ret)
@@ -1823,7 +1863,7 @@ static int crypt_iterate_devices(struct dm_target *ti,
 
 static struct target_type crypt_target = {
        .name   = "crypt",
-       .version = {1, 10, 0},
+       .version = {1, 11, 0},
        .module = THIS_MODULE,
        .ctr    = crypt_ctr,
        .dtr    = crypt_dtr,
index ea790623c30ba0d7522905d6f77f7db95c0cdebd..89f73ca22cfa112e7c215f9ab5c846de80dd5da4 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2003 Sistina Software (UK) Limited.
- * Copyright (C) 2004, 2010 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004, 2010-2011 Red Hat, Inc. All rights reserved.
  *
  * This file is released under the GPL.
  */
@@ -15,6 +15,9 @@
 
 #define DM_MSG_PREFIX "flakey"
 
+#define all_corrupt_bio_flags_match(bio, fc)   \
+       (((bio)->bi_rw & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags)
+
 /*
  * Flakey: Used for testing only, simulates intermittent,
  * catastrophic device failure.
@@ -25,60 +28,189 @@ struct flakey_c {
        sector_t start;
        unsigned up_interval;
        unsigned down_interval;
+       unsigned long flags;
+       unsigned corrupt_bio_byte;
+       unsigned corrupt_bio_rw;
+       unsigned corrupt_bio_value;
+       unsigned corrupt_bio_flags;
+};
+
+enum feature_flag_bits {
+       DROP_WRITES
 };
 
+static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
+                         struct dm_target *ti)
+{
+       int r;
+       unsigned argc;
+       const char *arg_name;
+
+       static struct dm_arg _args[] = {
+               {0, 6, "Invalid number of feature args"},
+               {1, UINT_MAX, "Invalid corrupt bio byte"},
+               {0, 255, "Invalid corrupt value to write into bio byte (0-255)"},
+               {0, UINT_MAX, "Invalid corrupt bio flags mask"},
+       };
+
+       /* No feature arguments supplied. */
+       if (!as->argc)
+               return 0;
+
+       r = dm_read_arg_group(_args, as, &argc, &ti->error);
+       if (r)
+               return r;
+
+       while (argc) {
+               arg_name = dm_shift_arg(as);
+               argc--;
+
+               /*
+                * drop_writes
+                */
+               if (!strcasecmp(arg_name, "drop_writes")) {
+                       if (test_and_set_bit(DROP_WRITES, &fc->flags)) {
+                               ti->error = "Feature drop_writes duplicated";
+                               return -EINVAL;
+                       }
+
+                       continue;
+               }
+
+               /*
+                * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>
+                */
+               if (!strcasecmp(arg_name, "corrupt_bio_byte")) {
+                       if (!argc)
+                               ti->error = "Feature corrupt_bio_byte requires parameters";
+
+                       r = dm_read_arg(_args + 1, as, &fc->corrupt_bio_byte, &ti->error);
+                       if (r)
+                               return r;
+                       argc--;
+
+                       /*
+                        * Direction r or w?
+                        */
+                       arg_name = dm_shift_arg(as);
+                       if (!strcasecmp(arg_name, "w"))
+                               fc->corrupt_bio_rw = WRITE;
+                       else if (!strcasecmp(arg_name, "r"))
+                               fc->corrupt_bio_rw = READ;
+                       else {
+                               ti->error = "Invalid corrupt bio direction (r or w)";
+                               return -EINVAL;
+                       }
+                       argc--;
+
+                       /*
+                        * Value of byte (0-255) to write in place of correct one.
+                        */
+                       r = dm_read_arg(_args + 2, as, &fc->corrupt_bio_value, &ti->error);
+                       if (r)
+                               return r;
+                       argc--;
+
+                       /*
+                        * Only corrupt bios with these flags set.
+                        */
+                       r = dm_read_arg(_args + 3, as, &fc->corrupt_bio_flags, &ti->error);
+                       if (r)
+                               return r;
+                       argc--;
+
+                       continue;
+               }
+
+               ti->error = "Unrecognised flakey feature requested";
+               return -EINVAL;
+       }
+
+       if (test_bit(DROP_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {
+               ti->error = "drop_writes is incompatible with corrupt_bio_byte with the WRITE flag set";
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 /*
- * Construct a flakey mapping: <dev_path> <offset> <up interval> <down interval>
+ * Construct a flakey mapping:
+ * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
+ *
+ *   Feature args:
+ *     [drop_writes]
+ *     [corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>]
+ *
+ *   Nth_byte starts from 1 for the first byte.
+ *   Direction is r for READ or w for WRITE.
+ *   bio_flags is ignored if 0.
  */
 static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 {
+       static struct dm_arg _args[] = {
+               {0, UINT_MAX, "Invalid up interval"},
+               {0, UINT_MAX, "Invalid down interval"},
+       };
+
+       int r;
        struct flakey_c *fc;
-       unsigned long long tmp;
+       unsigned long long tmpll;
+       struct dm_arg_set as;
+       const char *devname;
 
-       if (argc != 4) {
-               ti->error = "dm-flakey: Invalid argument count";
+       as.argc = argc;
+       as.argv = argv;
+
+       if (argc < 4) {
+               ti->error = "Invalid argument count";
                return -EINVAL;
        }
 
-       fc = kmalloc(sizeof(*fc), GFP_KERNEL);
+       fc = kzalloc(sizeof(*fc), GFP_KERNEL);
        if (!fc) {
-               ti->error = "dm-flakey: Cannot allocate linear context";
+               ti->error = "Cannot allocate linear context";
                return -ENOMEM;
        }
        fc->start_time = jiffies;
 
-       if (sscanf(argv[1], "%llu", &tmp) != 1) {
-               ti->error = "dm-flakey: Invalid device sector";
+       devname = dm_shift_arg(&as);
+
+       if (sscanf(dm_shift_arg(&as), "%llu", &tmpll) != 1) {
+               ti->error = "Invalid device sector";
                goto bad;
        }
-       fc->start = tmp;
+       fc->start = tmpll;
 
-       if (sscanf(argv[2], "%u", &fc->up_interval) != 1) {
-               ti->error = "dm-flakey: Invalid up interval";
+       r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
+       if (r)
                goto bad;
-       }
 
-       if (sscanf(argv[3], "%u", &fc->down_interval) != 1) {
-               ti->error = "dm-flakey: Invalid down interval";
+       r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
+       if (r)
                goto bad;
-       }
 
        if (!(fc->up_interval + fc->down_interval)) {
-               ti->error = "dm-flakey: Total (up + down) interval is zero";
+               ti->error = "Total (up + down) interval is zero";
                goto bad;
        }
 
        if (fc->up_interval + fc->down_interval < fc->up_interval) {
-               ti->error = "dm-flakey: Interval overflow";
+               ti->error = "Interval overflow";
                goto bad;
        }
 
-       if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &fc->dev)) {
-               ti->error = "dm-flakey: Device lookup failed";
+       r = parse_features(&as, fc, ti);
+       if (r)
+               goto bad;
+
+       if (dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev)) {
+               ti->error = "Device lookup failed";
                goto bad;
        }
 
        ti->num_flush_requests = 1;
+       ti->num_discard_requests = 1;
        ti->private = fc;
        return 0;
 
@@ -99,7 +231,7 @@ static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector)
 {
        struct flakey_c *fc = ti->private;
 
-       return fc->start + (bi_sector - ti->begin);
+       return fc->start + dm_target_offset(ti, bi_sector);
 }
 
 static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
@@ -111,6 +243,25 @@ static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
                bio->bi_sector = flakey_map_sector(ti, bio->bi_sector);
 }
 
+static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)
+{
+       unsigned bio_bytes = bio_cur_bytes(bio);
+       char *data = bio_data(bio);
+
+       /*
+        * Overwrite the Nth byte of the data returned.
+        */
+       if (data && bio_bytes >= fc->corrupt_bio_byte) {
+               data[fc->corrupt_bio_byte - 1] = fc->corrupt_bio_value;
+
+               DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
+                       "(rw=%c bi_rw=%lu bi_sector=%llu cur_bytes=%u)\n",
+                       bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
+                       (bio_data_dir(bio) == WRITE) ? 'w' : 'r',
+                       bio->bi_rw, (unsigned long long)bio->bi_sector, bio_bytes);
+       }
+}
+
 static int flakey_map(struct dm_target *ti, struct bio *bio,
                      union map_info *map_context)
 {
@@ -119,18 +270,71 @@ static int flakey_map(struct dm_target *ti, struct bio *bio,
 
        /* Are we alive ? */
        elapsed = (jiffies - fc->start_time) / HZ;
-       if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval)
+       if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
+               /*
+                * Flag this bio as submitted while down.
+                */
+               map_context->ll = 1;
+
+               /*
+                * Map reads as normal.
+                */
+               if (bio_data_dir(bio) == READ)
+                       goto map_bio;
+
+               /*
+                * Drop writes?
+                */
+               if (test_bit(DROP_WRITES, &fc->flags)) {
+                       bio_endio(bio, 0);
+                       return DM_MAPIO_SUBMITTED;
+               }
+
+               /*
+                * Corrupt matching writes.
+                */
+               if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == WRITE)) {
+                       if (all_corrupt_bio_flags_match(bio, fc))
+                               corrupt_bio_data(bio, fc);
+                       goto map_bio;
+               }
+
+               /*
+                * By default, error all I/O.
+                */
                return -EIO;
+       }
 
+map_bio:
        flakey_map_bio(ti, bio);
 
        return DM_MAPIO_REMAPPED;
 }
 
+static int flakey_end_io(struct dm_target *ti, struct bio *bio,
+                        int error, union map_info *map_context)
+{
+       struct flakey_c *fc = ti->private;
+       unsigned bio_submitted_while_down = map_context->ll;
+
+       /*
+        * Corrupt successful READs while in down state.
+        * If flags were specified, only corrupt those that match.
+        */
+       if (!error && bio_submitted_while_down &&
+           (bio_data_dir(bio) == READ) && (fc->corrupt_bio_rw == READ) &&
+           all_corrupt_bio_flags_match(bio, fc))
+               corrupt_bio_data(bio, fc);
+
+       return error;
+}
+
 static int flakey_status(struct dm_target *ti, status_type_t type,
                         char *result, unsigned int maxlen)
 {
+       unsigned sz = 0;
        struct flakey_c *fc = ti->private;
+       unsigned drop_writes;
 
        switch (type) {
        case STATUSTYPE_INFO:
@@ -138,9 +342,22 @@ static int flakey_status(struct dm_target *ti, status_type_t type,
                break;
 
        case STATUSTYPE_TABLE:
-               snprintf(result, maxlen, "%s %llu %u %u", fc->dev->name,
-                        (unsigned long long)fc->start, fc->up_interval,
-                        fc->down_interval);
+               DMEMIT("%s %llu %u %u ", fc->dev->name,
+                      (unsigned long long)fc->start, fc->up_interval,
+                      fc->down_interval);
+
+               drop_writes = test_bit(DROP_WRITES, &fc->flags);
+               DMEMIT("%u ", drop_writes + (fc->corrupt_bio_byte > 0) * 5);
+
+               if (drop_writes)
+                       DMEMIT("drop_writes ");
+
+               if (fc->corrupt_bio_byte)
+                       DMEMIT("corrupt_bio_byte %u %c %u %u ",
+                              fc->corrupt_bio_byte,
+                              (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r',
+                              fc->corrupt_bio_value, fc->corrupt_bio_flags);
+
                break;
        }
        return 0;
@@ -177,11 +394,12 @@ static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_
 
 static struct target_type flakey_target = {
        .name   = "flakey",
-       .version = {1, 1, 0},
+       .version = {1, 2, 0},
        .module = THIS_MODULE,
        .ctr    = flakey_ctr,
        .dtr    = flakey_dtr,
        .map    = flakey_map,
+       .end_io = flakey_end_io,
        .status = flakey_status,
        .ioctl  = flakey_ioctl,
        .merge  = flakey_merge,
index 2067288f61f9b5e868da28c6c7c9694b9a5269cf..ad2eba40e3190e700eab3136b8af5dd150cbf208 100644 (file)
@@ -38,6 +38,8 @@ struct io {
        struct dm_io_client *client;
        io_notify_fn callback;
        void *context;
+       void *vma_invalidate_address;
+       unsigned long vma_invalidate_size;
 } __attribute__((aligned(DM_IO_MAX_REGIONS)));
 
 static struct kmem_cache *_dm_io_cache;
@@ -116,6 +118,10 @@ static void dec_count(struct io *io, unsigned int region, int error)
                set_bit(region, &io->error_bits);
 
        if (atomic_dec_and_test(&io->count)) {
+               if (io->vma_invalidate_size)
+                       invalidate_kernel_vmap_range(io->vma_invalidate_address,
+                                                    io->vma_invalidate_size);
+
                if (io->sleeper)
                        wake_up_process(io->sleeper);
 
@@ -159,6 +165,9 @@ struct dpages {
 
        unsigned context_u;
        void *context_ptr;
+
+       void *vma_invalidate_address;
+       unsigned long vma_invalidate_size;
 };
 
 /*
@@ -377,6 +386,9 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions,
        io->sleeper = current;
        io->client = client;
 
+       io->vma_invalidate_address = dp->vma_invalidate_address;
+       io->vma_invalidate_size = dp->vma_invalidate_size;
+
        dispatch_io(rw, num_regions, where, dp, io, 1);
 
        while (1) {
@@ -415,13 +427,21 @@ static int async_io(struct dm_io_client *client, unsigned int num_regions,
        io->callback = fn;
        io->context = context;
 
+       io->vma_invalidate_address = dp->vma_invalidate_address;
+       io->vma_invalidate_size = dp->vma_invalidate_size;
+
        dispatch_io(rw, num_regions, where, dp, io, 0);
        return 0;
 }
 
-static int dp_init(struct dm_io_request *io_req, struct dpages *dp)
+static int dp_init(struct dm_io_request *io_req, struct dpages *dp,
+                  unsigned long size)
 {
        /* Set up dpages based on memory type */
+
+       dp->vma_invalidate_address = NULL;
+       dp->vma_invalidate_size = 0;
+
        switch (io_req->mem.type) {
        case DM_IO_PAGE_LIST:
                list_dp_init(dp, io_req->mem.ptr.pl, io_req->mem.offset);
@@ -432,6 +452,11 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp)
                break;
 
        case DM_IO_VMA:
+               flush_kernel_vmap_range(io_req->mem.ptr.vma, size);
+               if ((io_req->bi_rw & RW_MASK) == READ) {
+                       dp->vma_invalidate_address = io_req->mem.ptr.vma;
+                       dp->vma_invalidate_size = size;
+               }
                vm_dp_init(dp, io_req->mem.ptr.vma);
                break;
 
@@ -460,7 +485,7 @@ int dm_io(struct dm_io_request *io_req, unsigned num_regions,
        int r;
        struct dpages dp;
 
-       r = dp_init(io_req, &dp);
+       r = dp_init(io_req, &dp, (unsigned long)where->count << SECTOR_SHIFT);
        if (r)
                return r;
 
index 4cacdad2270a345e1ef8455eddf5c2a2838a22e1..2e9a3ca37bdd39c6cbaf36f800b77aa3a04a638f 100644 (file)
@@ -128,6 +128,24 @@ static struct hash_cell *__get_uuid_cell(const char *str)
        return NULL;
 }
 
+static struct hash_cell *__get_dev_cell(uint64_t dev)
+{
+       struct mapped_device *md;
+       struct hash_cell *hc;
+
+       md = dm_get_md(huge_decode_dev(dev));
+       if (!md)
+               return NULL;
+
+       hc = dm_get_mdptr(md);
+       if (!hc) {
+               dm_put(md);
+               return NULL;
+       }
+
+       return hc;
+}
+
 /*-----------------------------------------------------------------
  * Inserting, removing and renaming a device.
  *---------------------------------------------------------------*/
@@ -718,25 +736,45 @@ static int dev_create(struct dm_ioctl *param, size_t param_size)
  */
 static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
 {
-       struct mapped_device *md;
-       void *mdptr = NULL;
+       struct hash_cell *hc = NULL;
 
-       if (*param->uuid)
-               return __get_uuid_cell(param->uuid);
+       if (*param->uuid) {
+               if (*param->name || param->dev)
+                       return NULL;
 
-       if (*param->name)
-               return __get_name_cell(param->name);
+               hc = __get_uuid_cell(param->uuid);
+               if (!hc)
+                       return NULL;
+       } else if (*param->name) {
+               if (param->dev)
+                       return NULL;
 
-       md = dm_get_md(huge_decode_dev(param->dev));
-       if (!md)
-               goto out;
+               hc = __get_name_cell(param->name);
+               if (!hc)
+                       return NULL;
+       } else if (param->dev) {
+               hc = __get_dev_cell(param->dev);
+               if (!hc)
+                       return NULL;
+       } else
+               return NULL;
 
-       mdptr = dm_get_mdptr(md);
-       if (!mdptr)
-               dm_put(md);
+       /*
+        * Sneakily write in both the name and the uuid
+        * while we have the cell.
+        */
+       strlcpy(param->name, hc->name, sizeof(param->name));
+       if (hc->uuid)
+               strlcpy(param->uuid, hc->uuid, sizeof(param->uuid));
+       else
+               param->uuid[0] = '\0';
 
-out:
-       return mdptr;
+       if (hc->new_map)
+               param->flags |= DM_INACTIVE_PRESENT_FLAG;
+       else
+               param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
+
+       return hc;
 }
 
 static struct mapped_device *find_device(struct dm_ioctl *param)
@@ -746,24 +784,8 @@ static struct mapped_device *find_device(struct dm_ioctl *param)
 
        down_read(&_hash_lock);
        hc = __find_device_hash_cell(param);
-       if (hc) {
+       if (hc)
                md = hc->md;
-
-               /*
-                * Sneakily write in both the name and the uuid
-                * while we have the cell.
-                */
-               strlcpy(param->name, hc->name, sizeof(param->name));
-               if (hc->uuid)
-                       strlcpy(param->uuid, hc->uuid, sizeof(param->uuid));
-               else
-                       param->uuid[0] = '\0';
-
-               if (hc->new_map)
-                       param->flags |= DM_INACTIVE_PRESENT_FLAG;
-               else
-                       param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
-       }
        up_read(&_hash_lock);
 
        return md;
@@ -1402,6 +1424,11 @@ static int target_message(struct dm_ioctl *param, size_t param_size)
                goto out;
        }
 
+       if (!argc) {
+               DMWARN("Empty message received.");
+               goto out;
+       }
+
        table = dm_get_live_table(md);
        if (!table)
                goto out_argv;
index 320401dec1044215b16bd1c784c1fca2883b3537..f82147029636d87b30e990daa230dfb17ca69c50 100644 (file)
@@ -224,8 +224,6 @@ struct kcopyd_job {
        unsigned int num_dests;
        struct dm_io_region dests[DM_KCOPYD_MAX_REGIONS];
 
-       sector_t offset;
-       unsigned int nr_pages;
        struct page_list *pages;
 
        /*
@@ -380,7 +378,7 @@ static int run_io_job(struct kcopyd_job *job)
                .bi_rw = job->rw,
                .mem.type = DM_IO_PAGE_LIST,
                .mem.ptr.pl = job->pages,
-               .mem.offset = job->offset,
+               .mem.offset = 0,
                .notify.fn = complete_io,
                .notify.context = job,
                .client = job->kc->io_client,
@@ -397,10 +395,9 @@ static int run_io_job(struct kcopyd_job *job)
 static int run_pages_job(struct kcopyd_job *job)
 {
        int r;
+       unsigned nr_pages = dm_div_up(job->dests[0].count, PAGE_SIZE >> 9);
 
-       job->nr_pages = dm_div_up(job->dests[0].count + job->offset,
-                                 PAGE_SIZE >> 9);
-       r = kcopyd_get_pages(job->kc, job->nr_pages, &job->pages);
+       r = kcopyd_get_pages(job->kc, nr_pages, &job->pages);
        if (!r) {
                /* this job is ready for io */
                push(&job->kc->io_jobs, job);
@@ -602,8 +599,6 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
        job->num_dests = num_dests;
        memcpy(&job->dests, dests, sizeof(*dests) * num_dests);
 
-       job->offset = 0;
-       job->nr_pages = 0;
        job->pages = NULL;
 
        job->fn = fn;
@@ -622,6 +617,37 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
 }
 EXPORT_SYMBOL(dm_kcopyd_copy);
 
+void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc,
+                                dm_kcopyd_notify_fn fn, void *context)
+{
+       struct kcopyd_job *job;
+
+       job = mempool_alloc(kc->job_pool, GFP_NOIO);
+
+       memset(job, 0, sizeof(struct kcopyd_job));
+       job->kc = kc;
+       job->fn = fn;
+       job->context = context;
+
+       atomic_inc(&kc->nr_jobs);
+
+       return job;
+}
+EXPORT_SYMBOL(dm_kcopyd_prepare_callback);
+
+void dm_kcopyd_do_callback(void *j, int read_err, unsigned long write_err)
+{
+       struct kcopyd_job *job = j;
+       struct dm_kcopyd_client *kc = job->kc;
+
+       job->read_err = read_err;
+       job->write_err = write_err;
+
+       push(&kc->complete_jobs, job);
+       wake(kc);
+}
+EXPORT_SYMBOL(dm_kcopyd_do_callback);
+
 /*
  * Cancels a kcopyd job, eg. someone might be deactivating a
  * mirror.
index aa2e0c374ab3e0c985e6bdc6536807bc07bcf3ce..1021c89860116a5bb6e3a2faea1362c0ea805f52 100644 (file)
@@ -394,8 +394,7 @@ static int flush_by_group(struct log_c *lc, struct list_head *flush_list)
                        group[count] = fe->region;
                        count++;
 
-                       list_del(&fe->list);
-                       list_add(&fe->list, &tmp_list);
+                       list_move(&fe->list, &tmp_list);
 
                        type = fe->type;
                        if (count >= MAX_FLUSH_GROUP_COUNT)
index 948e3f4925bfe6d28a39aeed22e11324f1b1b8df..3b52bb72bd1f0cb8717798c121008a76784b9aa0 100644 (file)
@@ -197,15 +197,21 @@ EXPORT_SYMBOL(dm_dirty_log_destroy);
 #define MIRROR_DISK_VERSION 2
 #define LOG_OFFSET 2
 
-struct log_header {
-       uint32_t magic;
+struct log_header_disk {
+       __le32 magic;
 
        /*
         * Simple, incrementing version. no backward
         * compatibility.
         */
+       __le32 version;
+       __le64 nr_regions;
+} __packed;
+
+struct log_header_core {
+       uint32_t magic;
        uint32_t version;
-       sector_t nr_regions;
+       uint64_t nr_regions;
 };
 
 struct log_c {
@@ -239,10 +245,10 @@ struct log_c {
        int log_dev_failed;
        int log_dev_flush_failed;
        struct dm_dev *log_dev;
-       struct log_header header;
+       struct log_header_core header;
 
        struct dm_io_region header_location;
-       struct log_header *disk_header;
+       struct log_header_disk *disk_header;
 };
 
 /*
@@ -251,34 +257,34 @@ struct log_c {
  */
 static inline int log_test_bit(uint32_t *bs, unsigned bit)
 {
-       return test_bit_le(bit, (unsigned long *) bs) ? 1 : 0;
+       return test_bit_le(bit, bs) ? 1 : 0;
 }
 
 static inline void log_set_bit(struct log_c *l,
                               uint32_t *bs, unsigned bit)
 {
-       __test_and_set_bit_le(bit, (unsigned long *) bs);
+       __set_bit_le(bit, bs);
        l->touched_cleaned = 1;
 }
 
 static inline void log_clear_bit(struct log_c *l,
                                 uint32_t *bs, unsigned bit)
 {
-       __test_and_clear_bit_le(bit, (unsigned long *) bs);
+       __clear_bit_le(bit, bs);
        l->touched_dirtied = 1;
 }
 
 /*----------------------------------------------------------------
  * Header IO
  *--------------------------------------------------------------*/
-static void header_to_disk(struct log_header *core, struct log_header *disk)
+static void header_to_disk(struct log_header_core *core, struct log_header_disk *disk)
 {
        disk->magic = cpu_to_le32(core->magic);
        disk->version = cpu_to_le32(core->version);
        disk->nr_regions = cpu_to_le64(core->nr_regions);
 }
 
-static void header_from_disk(struct log_header *core, struct log_header *disk)
+static void header_from_disk(struct log_header_core *core, struct log_header_disk *disk)
 {
        core->magic = le32_to_cpu(disk->magic);
        core->version = le32_to_cpu(disk->version);
@@ -486,7 +492,7 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
        memset(lc->sync_bits, (sync == NOSYNC) ? -1 : 0, bitset_size);
        lc->sync_count = (sync == NOSYNC) ? region_count : 0;
 
-       lc->recovering_bits = vmalloc(bitset_size);
+       lc->recovering_bits = vzalloc(bitset_size);
        if (!lc->recovering_bits) {
                DMWARN("couldn't allocate sync bitset");
                vfree(lc->sync_bits);
@@ -498,7 +504,6 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
                kfree(lc);
                return -ENOMEM;
        }
-       memset(lc->recovering_bits, 0, bitset_size);
        lc->sync_search = 0;
        log->context = lc;
 
@@ -739,8 +744,7 @@ static int core_get_resync_work(struct dm_dirty_log *log, region_t *region)
                return 0;
 
        do {
-               *region = find_next_zero_bit_le(
-                                            (unsigned long *) lc->sync_bits,
+               *region = find_next_zero_bit_le(lc->sync_bits,
                                             lc->region_count,
                                             lc->sync_search);
                lc->sync_search = *region + 1;
index c3547016f0f1e7156d1b28b0fded62b5bf534008..5e0090ef4182e71de26afec04d23edb19b3f4e39 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/atomic.h>
 
 #define DM_MSG_PREFIX "multipath"
-#define MESG_STR(x) x, sizeof(x)
 #define DM_PG_INIT_DELAY_MSECS 2000
 #define DM_PG_INIT_DELAY_DEFAULT ((unsigned) -1)
 
@@ -505,80 +504,29 @@ static void trigger_event(struct work_struct *work)
  *      <#paths> <#per-path selector args>
  *         [<path> [<arg>]* ]+ ]+
  *---------------------------------------------------------------*/
-struct param {
-       unsigned min;
-       unsigned max;
-       char *error;
-};
-
-static int read_param(struct param *param, char *str, unsigned *v, char **error)
-{
-       if (!str ||
-           (sscanf(str, "%u", v) != 1) ||
-           (*v < param->min) ||
-           (*v > param->max)) {
-               *error = param->error;
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-struct arg_set {
-       unsigned argc;
-       char **argv;
-};
-
-static char *shift(struct arg_set *as)
-{
-       char *r;
-
-       if (as->argc) {
-               as->argc--;
-               r = *as->argv;
-               as->argv++;
-               return r;
-       }
-
-       return NULL;
-}
-
-static void consume(struct arg_set *as, unsigned n)
-{
-       BUG_ON (as->argc < n);
-       as->argc -= n;
-       as->argv += n;
-}
-
-static int parse_path_selector(struct arg_set *as, struct priority_group *pg,
+static int parse_path_selector(struct dm_arg_set *as, struct priority_group *pg,
                               struct dm_target *ti)
 {
        int r;
        struct path_selector_type *pst;
        unsigned ps_argc;
 
-       static struct param _params[] = {
+       static struct dm_arg _args[] = {
                {0, 1024, "invalid number of path selector args"},
        };
 
-       pst = dm_get_path_selector(shift(as));
+       pst = dm_get_path_selector(dm_shift_arg(as));
        if (!pst) {
                ti->error = "unknown path selector type";
                return -EINVAL;
        }
 
-       r = read_param(_params, shift(as), &ps_argc, &ti->error);
+       r = dm_read_arg_group(_args, as, &ps_argc, &ti->error);
        if (r) {
                dm_put_path_selector(pst);
                return -EINVAL;
        }
 
-       if (ps_argc > as->argc) {
-               dm_put_path_selector(pst);
-               ti->error = "not enough arguments for path selector";
-               return -EINVAL;
-       }
-
        r = pst->create(&pg->ps, ps_argc, as->argv);
        if (r) {
                dm_put_path_selector(pst);
@@ -587,12 +535,12 @@ static int parse_path_selector(struct arg_set *as, struct priority_group *pg,
        }
 
        pg->ps.type = pst;
-       consume(as, ps_argc);
+       dm_consume_args(as, ps_argc);
 
        return 0;
 }
 
-static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
+static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps,
                               struct dm_target *ti)
 {
        int r;
@@ -609,7 +557,7 @@ 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), dm_table_get_mode(ti->table),
+       r = dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table),
                          &p->path.dev);
        if (r) {
                ti->error = "error getting device";
@@ -660,16 +608,16 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
        return ERR_PTR(r);
 }
 
-static struct priority_group *parse_priority_group(struct arg_set *as,
+static struct priority_group *parse_priority_group(struct dm_arg_set *as,
                                                   struct multipath *m)
 {
-       static struct param _params[] = {
+       static struct dm_arg _args[] = {
                {1, 1024, "invalid number of paths"},
                {0, 1024, "invalid number of selector args"}
        };
 
        int r;
-       unsigned i, nr_selector_args, nr_params;
+       unsigned i, nr_selector_args, nr_args;
        struct priority_group *pg;
        struct dm_target *ti = m->ti;
 
@@ -693,26 +641,26 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
        /*
         * read the paths
         */
-       r = read_param(_params, shift(as), &pg->nr_pgpaths, &ti->error);
+       r = dm_read_arg(_args, as, &pg->nr_pgpaths, &ti->error);
        if (r)
                goto bad;
 
-       r = read_param(_params + 1, shift(as), &nr_selector_args, &ti->error);
+       r = dm_read_arg(_args + 1, as, &nr_selector_args, &ti->error);
        if (r)
                goto bad;
 
-       nr_params = 1 + nr_selector_args;
+       nr_args = 1 + nr_selector_args;
        for (i = 0; i < pg->nr_pgpaths; i++) {
                struct pgpath *pgpath;
-               struct arg_set path_args;
+               struct dm_arg_set path_args;
 
-               if (as->argc < nr_params) {
+               if (as->argc < nr_args) {
                        ti->error = "not enough path parameters";
                        r = -EINVAL;
                        goto bad;
                }
 
-               path_args.argc = nr_params;
+               path_args.argc = nr_args;
                path_args.argv = as->argv;
 
                pgpath = parse_path(&path_args, &pg->ps, ti);
@@ -723,7 +671,7 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
 
                pgpath->pg = pg;
                list_add_tail(&pgpath->list, &pg->pgpaths);
-               consume(as, nr_params);
+               dm_consume_args(as, nr_args);
        }
 
        return pg;
@@ -733,28 +681,23 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
        return ERR_PTR(r);
 }
 
-static int parse_hw_handler(struct arg_set *as, struct multipath *m)
+static int parse_hw_handler(struct dm_arg_set *as, struct multipath *m)
 {
        unsigned hw_argc;
        int ret;
        struct dm_target *ti = m->ti;
 
-       static struct param _params[] = {
+       static struct dm_arg _args[] = {
                {0, 1024, "invalid number of hardware handler args"},
        };
 
-       if (read_param(_params, shift(as), &hw_argc, &ti->error))
+       if (dm_read_arg_group(_args, as, &hw_argc, &ti->error))
                return -EINVAL;
 
        if (!hw_argc)
                return 0;
 
-       if (hw_argc > as->argc) {
-               ti->error = "not enough arguments for hardware handler";
-               return -EINVAL;
-       }
-
-       m->hw_handler_name = kstrdup(shift(as), GFP_KERNEL);
+       m->hw_handler_name = kstrdup(dm_shift_arg(as), GFP_KERNEL);
        request_module("scsi_dh_%s", m->hw_handler_name);
        if (scsi_dh_handler_exist(m->hw_handler_name) == 0) {
                ti->error = "unknown hardware handler type";
@@ -778,7 +721,7 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m)
                for (i = 0, p+=j+1; i <= hw_argc - 2; i++, p+=j+1)
                        j = sprintf(p, "%s", as->argv[i]);
        }
-       consume(as, hw_argc - 1);
+       dm_consume_args(as, hw_argc - 1);
 
        return 0;
 fail:
@@ -787,20 +730,20 @@ fail:
        return ret;
 }
 
-static int parse_features(struct arg_set *as, struct multipath *m)
+static int parse_features(struct dm_arg_set *as, struct multipath *m)
 {
        int r;
        unsigned argc;
        struct dm_target *ti = m->ti;
-       const char *param_name;
+       const char *arg_name;
 
-       static struct param _params[] = {
+       static struct dm_arg _args[] = {
                {0, 5, "invalid number of feature args"},
                {1, 50, "pg_init_retries must be between 1 and 50"},
                {0, 60000, "pg_init_delay_msecs must be between 0 and 60000"},
        };
 
-       r = read_param(_params, shift(as), &argc, &ti->error);
+       r = dm_read_arg_group(_args, as, &argc, &ti->error);
        if (r)
                return -EINVAL;
 
@@ -808,26 +751,24 @@ static int parse_features(struct arg_set *as, struct multipath *m)
                return 0;
 
        do {
-               param_name = shift(as);
+               arg_name = dm_shift_arg(as);
                argc--;
 
-               if (!strnicmp(param_name, MESG_STR("queue_if_no_path"))) {
+               if (!strcasecmp(arg_name, "queue_if_no_path")) {
                        r = queue_if_no_path(m, 1, 0);
                        continue;
                }
 
-               if (!strnicmp(param_name, MESG_STR("pg_init_retries")) &&
+               if (!strcasecmp(arg_name, "pg_init_retries") &&
                    (argc >= 1)) {
-                       r = read_param(_params + 1, shift(as),
-                                      &m->pg_init_retries, &ti->error);
+                       r = dm_read_arg(_args + 1, as, &m->pg_init_retries, &ti->error);
                        argc--;
                        continue;
                }
 
-               if (!strnicmp(param_name, MESG_STR("pg_init_delay_msecs")) &&
+               if (!strcasecmp(arg_name, "pg_init_delay_msecs") &&
                    (argc >= 1)) {
-                       r = read_param(_params + 2, shift(as),
-                                      &m->pg_init_delay_msecs, &ti->error);
+                       r = dm_read_arg(_args + 2, as, &m->pg_init_delay_msecs, &ti->error);
                        argc--;
                        continue;
                }
@@ -842,15 +783,15 @@ static int parse_features(struct arg_set *as, struct multipath *m)
 static int multipath_ctr(struct dm_target *ti, unsigned int argc,
                         char **argv)
 {
-       /* target parameters */
-       static struct param _params[] = {
+       /* target arguments */
+       static struct dm_arg _args[] = {
                {0, 1024, "invalid number of priority groups"},
                {0, 1024, "invalid initial priority group number"},
        };
 
        int r;
        struct multipath *m;
-       struct arg_set as;
+       struct dm_arg_set as;
        unsigned pg_count = 0;
        unsigned next_pg_num;
 
@@ -871,11 +812,11 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
        if (r)
                goto bad;
 
-       r = read_param(_params, shift(&as), &m->nr_priority_groups, &ti->error);
+       r = dm_read_arg(_args, &as, &m->nr_priority_groups, &ti->error);
        if (r)
                goto bad;
 
-       r = read_param(_params + 1, shift(&as), &next_pg_num, &ti->error);
+       r = dm_read_arg(_args + 1, &as, &next_pg_num, &ti->error);
        if (r)
                goto bad;
 
@@ -1505,10 +1446,10 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
        }
 
        if (argc == 1) {
-               if (!strnicmp(argv[0], MESG_STR("queue_if_no_path"))) {
+               if (!strcasecmp(argv[0], "queue_if_no_path")) {
                        r = queue_if_no_path(m, 1, 0);
                        goto out;
-               } else if (!strnicmp(argv[0], MESG_STR("fail_if_no_path"))) {
+               } else if (!strcasecmp(argv[0], "fail_if_no_path")) {
                        r = queue_if_no_path(m, 0, 0);
                        goto out;
                }
@@ -1519,18 +1460,18 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
                goto out;
        }
 
-       if (!strnicmp(argv[0], MESG_STR("disable_group"))) {
+       if (!strcasecmp(argv[0], "disable_group")) {
                r = bypass_pg_num(m, argv[1], 1);
                goto out;
-       } else if (!strnicmp(argv[0], MESG_STR("enable_group"))) {
+       } else if (!strcasecmp(argv[0], "enable_group")) {
                r = bypass_pg_num(m, argv[1], 0);
                goto out;
-       } else if (!strnicmp(argv[0], MESG_STR("switch_group"))) {
+       } else if (!strcasecmp(argv[0], "switch_group")) {
                r = switch_pg_num(m, argv[1]);
                goto out;
-       } else if (!strnicmp(argv[0], MESG_STR("reinstate_path")))
+       } else if (!strcasecmp(argv[0], "reinstate_path"))
                action = reinstate_path;
-       else if (!strnicmp(argv[0], MESG_STR("fail_path")))
+       else if (!strcasecmp(argv[0], "fail_path"))
                action = fail_path;
        else {
                DMWARN("Unrecognised multipath message received.");
index e5d8904fc8f647162d4a7d79150491797cfda6d2..a002dd85db1e674e2efbc188a531f001c2d0716b 100644 (file)
@@ -8,19 +8,19 @@
 #include <linux/slab.h>
 
 #include "md.h"
+#include "raid1.h"
 #include "raid5.h"
-#include "dm.h"
 #include "bitmap.h"
 
+#include <linux/device-mapper.h>
+
 #define DM_MSG_PREFIX "raid"
 
 /*
- * If the MD doesn't support MD_SYNC_STATE_FORCED yet, then
- * make it so the flag doesn't set anything.
+ * The following flags are used by dm-raid.c to set up the array state.
+ * They must be cleared before md_run is called.
  */
-#ifndef MD_SYNC_STATE_FORCED
-#define MD_SYNC_STATE_FORCED 0
-#endif
+#define FirstUse 10             /* rdev flag */
 
 struct raid_dev {
        /*
@@ -43,14 +43,15 @@ struct raid_dev {
 /*
  * Flags for rs->print_flags field.
  */
-#define DMPF_DAEMON_SLEEP      0x1
-#define DMPF_MAX_WRITE_BEHIND  0x2
-#define DMPF_SYNC              0x4
-#define DMPF_NOSYNC            0x8
-#define DMPF_STRIPE_CACHE      0x10
-#define DMPF_MIN_RECOVERY_RATE 0x20
-#define DMPF_MAX_RECOVERY_RATE 0x40
-
+#define DMPF_SYNC              0x1
+#define DMPF_NOSYNC            0x2
+#define DMPF_REBUILD           0x4
+#define DMPF_DAEMON_SLEEP      0x8
+#define DMPF_MIN_RECOVERY_RATE 0x10
+#define DMPF_MAX_RECOVERY_RATE 0x20
+#define DMPF_MAX_WRITE_BEHIND  0x40
+#define DMPF_STRIPE_CACHE      0x80
+#define DMPF_REGION_SIZE       0X100
 struct raid_set {
        struct dm_target *ti;
 
@@ -72,6 +73,7 @@ static struct raid_type {
        const unsigned level;           /* RAID level. */
        const unsigned algorithm;       /* RAID algorithm. */
 } raid_types[] = {
+       {"raid1",    "RAID1 (mirroring)",               0, 2, 1, 0 /* NONE */},
        {"raid4",    "RAID4 (dedicated parity disk)",   1, 2, 5, ALGORITHM_PARITY_0},
        {"raid5_la", "RAID5 (left asymmetric)",         1, 2, 5, ALGORITHM_LEFT_ASYMMETRIC},
        {"raid5_ra", "RAID5 (right asymmetric)",        1, 2, 5, ALGORITHM_RIGHT_ASYMMETRIC},
@@ -105,7 +107,8 @@ static struct raid_set *context_alloc(struct dm_target *ti, struct raid_type *ra
        }
 
        sectors_per_dev = ti->len;
-       if (sector_div(sectors_per_dev, (raid_devs - raid_type->parity_devs))) {
+       if ((raid_type->level > 1) &&
+           sector_div(sectors_per_dev, (raid_devs - raid_type->parity_devs))) {
                ti->error = "Target length not divisible by number of data devices";
                return ERR_PTR(-EINVAL);
        }
@@ -147,9 +150,16 @@ static void context_free(struct raid_set *rs)
 {
        int i;
 
-       for (i = 0; i < rs->md.raid_disks; i++)
+       for (i = 0; i < rs->md.raid_disks; i++) {
+               if (rs->dev[i].meta_dev)
+                       dm_put_device(rs->ti, rs->dev[i].meta_dev);
+               if (rs->dev[i].rdev.sb_page)
+                       put_page(rs->dev[i].rdev.sb_page);
+               rs->dev[i].rdev.sb_page = NULL;
+               rs->dev[i].rdev.sb_loaded = 0;
                if (rs->dev[i].data_dev)
                        dm_put_device(rs->ti, rs->dev[i].data_dev);
+       }
 
        kfree(rs);
 }
@@ -159,7 +169,16 @@ static void context_free(struct raid_set *rs)
  *  <meta_dev>: meta device name or '-' if missing
  *  <data_dev>: data device name or '-' if missing
  *
- * This code parses those words.
+ * The following are permitted:
+ *    - -
+ *    - <data_dev>
+ *    <meta_dev> <data_dev>
+ *
+ * The following is not allowed:
+ *    <meta_dev> -
+ *
+ * This code parses those words.  If there is a failure,
+ * the caller must use context_free to unwind the operations.
  */
 static int dev_parms(struct raid_set *rs, char **argv)
 {
@@ -182,8 +201,16 @@ static int dev_parms(struct raid_set *rs, char **argv)
                rs->dev[i].rdev.mddev = &rs->md;
 
                if (strcmp(argv[0], "-")) {
-                       rs->ti->error = "Metadata devices not supported";
-                       return -EINVAL;
+                       ret = dm_get_device(rs->ti, argv[0],
+                                           dm_table_get_mode(rs->ti->table),
+                                           &rs->dev[i].meta_dev);
+                       rs->ti->error = "RAID metadata device lookup failure";
+                       if (ret)
+                               return ret;
+
+                       rs->dev[i].rdev.sb_page = alloc_page(GFP_KERNEL);
+                       if (!rs->dev[i].rdev.sb_page)
+                               return -ENOMEM;
                }
 
                if (!strcmp(argv[1], "-")) {
@@ -193,6 +220,10 @@ static int dev_parms(struct raid_set *rs, char **argv)
                                return -EINVAL;
                        }
 
+                       rs->ti->error = "No data device supplied with metadata device";
+                       if (rs->dev[i].meta_dev)
+                               return -EINVAL;
+
                        continue;
                }
 
@@ -204,6 +235,10 @@ static int dev_parms(struct raid_set *rs, char **argv)
                        return ret;
                }
 
+               if (rs->dev[i].meta_dev) {
+                       metadata_available = 1;
+                       rs->dev[i].rdev.meta_bdev = rs->dev[i].meta_dev->bdev;
+               }
                rs->dev[i].rdev.bdev = rs->dev[i].data_dev->bdev;
                list_add(&rs->dev[i].rdev.same_set, &rs->md.disks);
                if (!test_bit(In_sync, &rs->dev[i].rdev.flags))
@@ -234,34 +269,110 @@ static int dev_parms(struct raid_set *rs, char **argv)
        return 0;
 }
 
+/*
+ * validate_region_size
+ * @rs
+ * @region_size:  region size in sectors.  If 0, pick a size (4MiB default).
+ *
+ * Set rs->md.bitmap_info.chunksize (which really refers to 'region size').
+ * Ensure that (ti->len/region_size < 2^21) - required by MD bitmap.
+ *
+ * Returns: 0 on success, -EINVAL on failure.
+ */
+static int validate_region_size(struct raid_set *rs, unsigned long region_size)
+{
+       unsigned long min_region_size = rs->ti->len / (1 << 21);
+
+       if (!region_size) {
+               /*
+                * Choose a reasonable default.  All figures in sectors.
+                */
+               if (min_region_size > (1 << 13)) {
+                       DMINFO("Choosing default region size of %lu sectors",
+                              region_size);
+                       region_size = min_region_size;
+               } else {
+                       DMINFO("Choosing default region size of 4MiB");
+                       region_size = 1 << 13; /* sectors */
+               }
+       } else {
+               /*
+                * Validate user-supplied value.
+                */
+               if (region_size > rs->ti->len) {
+                       rs->ti->error = "Supplied region size is too large";
+                       return -EINVAL;
+               }
+
+               if (region_size < min_region_size) {
+                       DMERR("Supplied region_size (%lu sectors) below minimum (%lu)",
+                             region_size, min_region_size);
+                       rs->ti->error = "Supplied region size is too small";
+                       return -EINVAL;
+               }
+
+               if (!is_power_of_2(region_size)) {
+                       rs->ti->error = "Region size is not a power of 2";
+                       return -EINVAL;
+               }
+
+               if (region_size < rs->md.chunk_sectors) {
+                       rs->ti->error = "Region size is smaller than the chunk size";
+                       return -EINVAL;
+               }
+       }
+
+       /*
+        * Convert sectors to bytes.
+        */
+       rs->md.bitmap_info.chunksize = (region_size << 9);
+
+       return 0;
+}
+
 /*
  * Possible arguments are...
- * RAID456:
  *     <chunk_size> [optional_args]
  *
- * Optional args:
- *    [[no]sync]                       Force or prevent recovery of the entire array
+ * Argument definitions
+ *    <chunk_size>                     The number of sectors per disk that
+ *                                      will form the "stripe"
+ *    [[no]sync]                       Force or prevent recovery of the
+ *                                      entire array
  *    [rebuild <idx>]                  Rebuild the drive indicated by the index
- *    [daemon_sleep <ms>]              Time between bitmap daemon work to clear bits
+ *    [daemon_sleep <ms>]              Time between bitmap daemon work to
+ *                                      clear bits
  *    [min_recovery_rate <kB/sec/disk>]        Throttle RAID initialization
  *    [max_recovery_rate <kB/sec/disk>]        Throttle RAID initialization
+ *    [write_mostly <idx>]             Indicate a write mostly drive via index
  *    [max_write_behind <sectors>]     See '-write-behind=' (man mdadm)
  *    [stripe_cache <sectors>]         Stripe cache size for higher RAIDs
+ *    [region_size <sectors>]           Defines granularity of bitmap
  */
 static int parse_raid_params(struct raid_set *rs, char **argv,
                             unsigned num_raid_params)
 {
        unsigned i, rebuild_cnt = 0;
-       unsigned long value;
+       unsigned long value, region_size = 0;
        char *key;
 
        /*
         * First, parse the in-order required arguments
+        * "chunk_size" is the only argument of this type.
         */
-       if ((strict_strtoul(argv[0], 10, &value) < 0) ||
-           !is_power_of_2(value) || (value < 8)) {
+       if ((strict_strtoul(argv[0], 10, &value) < 0)) {
                rs->ti->error = "Bad chunk size";
                return -EINVAL;
+       } else if (rs->raid_type->level == 1) {
+               if (value)
+                       DMERR("Ignoring chunk size parameter for RAID 1");
+               value = 0;
+       } else if (!is_power_of_2(value)) {
+               rs->ti->error = "Chunk size must be a power of 2";
+               return -EINVAL;
+       } else if (value < 8) {
+               rs->ti->error = "Chunk size value is too small";
+               return -EINVAL;
        }
 
        rs->md.new_chunk_sectors = rs->md.chunk_sectors = value;
@@ -269,22 +380,39 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
        num_raid_params--;
 
        /*
-        * Second, parse the unordered optional arguments
+        * We set each individual device as In_sync with a completed
+        * 'recovery_offset'.  If there has been a device failure or
+        * replacement then one of the following cases applies:
+        *
+        *   1) User specifies 'rebuild'.
+        *      - Device is reset when param is read.
+        *   2) A new device is supplied.
+        *      - No matching superblock found, resets device.
+        *   3) Device failure was transient and returns on reload.
+        *      - Failure noticed, resets device for bitmap replay.
+        *   4) Device hadn't completed recovery after previous failure.
+        *      - Superblock is read and overrides recovery_offset.
+        *
+        * What is found in the superblocks of the devices is always
+        * authoritative, unless 'rebuild' or '[no]sync' was specified.
         */
-       for (i = 0; i < rs->md.raid_disks; i++)
+       for (i = 0; i < rs->md.raid_disks; i++) {
                set_bit(In_sync, &rs->dev[i].rdev.flags);
+               rs->dev[i].rdev.recovery_offset = MaxSector;
+       }
 
+       /*
+        * Second, parse the unordered optional arguments
+        */
        for (i = 0; i < num_raid_params; i++) {
-               if (!strcmp(argv[i], "nosync")) {
+               if (!strcasecmp(argv[i], "nosync")) {
                        rs->md.recovery_cp = MaxSector;
                        rs->print_flags |= DMPF_NOSYNC;
-                       rs->md.flags |= MD_SYNC_STATE_FORCED;
                        continue;
                }
-               if (!strcmp(argv[i], "sync")) {
+               if (!strcasecmp(argv[i], "sync")) {
                        rs->md.recovery_cp = 0;
                        rs->print_flags |= DMPF_SYNC;
-                       rs->md.flags |= MD_SYNC_STATE_FORCED;
                        continue;
                }
 
@@ -300,9 +428,13 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                        return -EINVAL;
                }
 
-               if (!strcmp(key, "rebuild")) {
-                       if (++rebuild_cnt > rs->raid_type->parity_devs) {
-                               rs->ti->error = "Too many rebuild drives given";
+               if (!strcasecmp(key, "rebuild")) {
+                       rebuild_cnt++;
+                       if (((rs->raid_type->level != 1) &&
+                            (rebuild_cnt > rs->raid_type->parity_devs)) ||
+                           ((rs->raid_type->level == 1) &&
+                            (rebuild_cnt > (rs->md.raid_disks - 1)))) {
+                               rs->ti->error = "Too many rebuild devices specified for given RAID type";
                                return -EINVAL;
                        }
                        if (value > rs->md.raid_disks) {
@@ -311,7 +443,22 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                        }
                        clear_bit(In_sync, &rs->dev[value].rdev.flags);
                        rs->dev[value].rdev.recovery_offset = 0;
-               } else if (!strcmp(key, "max_write_behind")) {
+                       rs->print_flags |= DMPF_REBUILD;
+               } else if (!strcasecmp(key, "write_mostly")) {
+                       if (rs->raid_type->level != 1) {
+                               rs->ti->error = "write_mostly option is only valid for RAID1";
+                               return -EINVAL;
+                       }
+                       if (value > rs->md.raid_disks) {
+                               rs->ti->error = "Invalid write_mostly drive index given";
+                               return -EINVAL;
+                       }
+                       set_bit(WriteMostly, &rs->dev[value].rdev.flags);
+               } else if (!strcasecmp(key, "max_write_behind")) {
+                       if (rs->raid_type->level != 1) {
+                               rs->ti->error = "max_write_behind option is only valid for RAID1";
+                               return -EINVAL;
+                       }
                        rs->print_flags |= DMPF_MAX_WRITE_BEHIND;
 
                        /*
@@ -324,14 +471,14 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                                return -EINVAL;
                        }
                        rs->md.bitmap_info.max_write_behind = value;
-               } else if (!strcmp(key, "daemon_sleep")) {
+               } else if (!strcasecmp(key, "daemon_sleep")) {
                        rs->print_flags |= DMPF_DAEMON_SLEEP;
                        if (!value || (value > MAX_SCHEDULE_TIMEOUT)) {
                                rs->ti->error = "daemon sleep period out of range";
                                return -EINVAL;
                        }
                        rs->md.bitmap_info.daemon_sleep = value;
-               } else if (!strcmp(key, "stripe_cache")) {
+               } else if (!strcasecmp(key, "stripe_cache")) {
                        rs->print_flags |= DMPF_STRIPE_CACHE;
 
                        /*
@@ -348,20 +495,23 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                                rs->ti->error = "Bad stripe_cache size";
                                return -EINVAL;
                        }
-               } else if (!strcmp(key, "min_recovery_rate")) {
+               } else if (!strcasecmp(key, "min_recovery_rate")) {
                        rs->print_flags |= DMPF_MIN_RECOVERY_RATE;
                        if (value > INT_MAX) {
                                rs->ti->error = "min_recovery_rate out of range";
                                return -EINVAL;
                        }
                        rs->md.sync_speed_min = (int)value;
-               } else if (!strcmp(key, "max_recovery_rate")) {
+               } else if (!strcasecmp(key, "max_recovery_rate")) {
                        rs->print_flags |= DMPF_MAX_RECOVERY_RATE;
                        if (value > INT_MAX) {
                                rs->ti->error = "max_recovery_rate out of range";
                                return -EINVAL;
                        }
                        rs->md.sync_speed_max = (int)value;
+               } else if (!strcasecmp(key, "region_size")) {
+                       rs->print_flags |= DMPF_REGION_SIZE;
+                       region_size = value;
                } else {
                        DMERR("Unable to parse RAID parameter: %s", key);
                        rs->ti->error = "Unable to parse RAID parameters";
@@ -369,6 +519,19 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                }
        }
 
+       if (validate_region_size(rs, region_size))
+               return -EINVAL;
+
+       if (rs->md.chunk_sectors)
+               rs->ti->split_io = rs->md.chunk_sectors;
+       else
+               rs->ti->split_io = region_size;
+
+       if (rs->md.chunk_sectors)
+               rs->ti->split_io = rs->md.chunk_sectors;
+       else
+               rs->ti->split_io = region_size;
+
        /* Assume there are no metadata devices until the drives are parsed */
        rs->md.persistent = 0;
        rs->md.external = 1;
@@ -387,17 +550,351 @@ static int raid_is_congested(struct dm_target_callbacks *cb, int bits)
 {
        struct raid_set *rs = container_of(cb, struct raid_set, callbacks);
 
+       if (rs->raid_type->level == 1)
+               return md_raid1_congested(&rs->md, bits);
+
        return md_raid5_congested(&rs->md, bits);
 }
 
+/*
+ * This structure is never routinely used by userspace, unlike md superblocks.
+ * Devices with this superblock should only ever be accessed via device-mapper.
+ */
+#define DM_RAID_MAGIC 0x64526D44
+struct dm_raid_superblock {
+       __le32 magic;           /* "DmRd" */
+       __le32 features;        /* Used to indicate possible future changes */
+
+       __le32 num_devices;     /* Number of devices in this array. (Max 64) */
+       __le32 array_position;  /* The position of this drive in the array */
+
+       __le64 events;          /* Incremented by md when superblock updated */
+       __le64 failed_devices;  /* Bit field of devices to indicate failures */
+
+       /*
+        * This offset tracks the progress of the repair or replacement of
+        * an individual drive.
+        */
+       __le64 disk_recovery_offset;
+
+       /*
+        * This offset tracks the progress of the initial array
+        * synchronisation/parity calculation.
+        */
+       __le64 array_resync_offset;
+
+       /*
+        * RAID characteristics
+        */
+       __le32 level;
+       __le32 layout;
+       __le32 stripe_sectors;
+
+       __u8 pad[452];          /* Round struct to 512 bytes. */
+                               /* Always set to 0 when writing. */
+} __packed;
+
+static int read_disk_sb(mdk_rdev_t *rdev, int size)
+{
+       BUG_ON(!rdev->sb_page);
+
+       if (rdev->sb_loaded)
+               return 0;
+
+       if (!sync_page_io(rdev, 0, size, rdev->sb_page, READ, 1)) {
+               DMERR("Failed to read device superblock");
+               return -EINVAL;
+       }
+
+       rdev->sb_loaded = 1;
+
+       return 0;
+}
+
+static void super_sync(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+       mdk_rdev_t *r, *t;
+       uint64_t failed_devices;
+       struct dm_raid_superblock *sb;
+
+       sb = page_address(rdev->sb_page);
+       failed_devices = le64_to_cpu(sb->failed_devices);
+
+       rdev_for_each(r, t, mddev)
+               if ((r->raid_disk >= 0) && test_bit(Faulty, &r->flags))
+                       failed_devices |= (1ULL << r->raid_disk);
+
+       memset(sb, 0, sizeof(*sb));
+
+       sb->magic = cpu_to_le32(DM_RAID_MAGIC);
+       sb->features = cpu_to_le32(0);  /* No features yet */
+
+       sb->num_devices = cpu_to_le32(mddev->raid_disks);
+       sb->array_position = cpu_to_le32(rdev->raid_disk);
+
+       sb->events = cpu_to_le64(mddev->events);
+       sb->failed_devices = cpu_to_le64(failed_devices);
+
+       sb->disk_recovery_offset = cpu_to_le64(rdev->recovery_offset);
+       sb->array_resync_offset = cpu_to_le64(mddev->recovery_cp);
+
+       sb->level = cpu_to_le32(mddev->level);
+       sb->layout = cpu_to_le32(mddev->layout);
+       sb->stripe_sectors = cpu_to_le32(mddev->chunk_sectors);
+}
+
+/*
+ * super_load
+ *
+ * This function creates a superblock if one is not found on the device
+ * and will decide which superblock to use if there's a choice.
+ *
+ * Return: 1 if use rdev, 0 if use refdev, -Exxx otherwise
+ */
+static int super_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev)
+{
+       int ret;
+       struct dm_raid_superblock *sb;
+       struct dm_raid_superblock *refsb;
+       uint64_t events_sb, events_refsb;
+
+       rdev->sb_start = 0;
+       rdev->sb_size = sizeof(*sb);
+
+       ret = read_disk_sb(rdev, rdev->sb_size);
+       if (ret)
+               return ret;
+
+       sb = page_address(rdev->sb_page);
+       if (sb->magic != cpu_to_le32(DM_RAID_MAGIC)) {
+               super_sync(rdev->mddev, rdev);
+
+               set_bit(FirstUse, &rdev->flags);
+
+               /* Force writing of superblocks to disk */
+               set_bit(MD_CHANGE_DEVS, &rdev->mddev->flags);
+
+               /* Any superblock is better than none, choose that if given */
+               return refdev ? 0 : 1;
+       }
+
+       if (!refdev)
+               return 1;
+
+       events_sb = le64_to_cpu(sb->events);
+
+       refsb = page_address(refdev->sb_page);
+       events_refsb = le64_to_cpu(refsb->events);
+
+       return (events_sb > events_refsb) ? 1 : 0;
+}
+
+static int super_init_validation(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+       int role;
+       struct raid_set *rs = container_of(mddev, struct raid_set, md);
+       uint64_t events_sb;
+       uint64_t failed_devices;
+       struct dm_raid_superblock *sb;
+       uint32_t new_devs = 0;
+       uint32_t rebuilds = 0;
+       mdk_rdev_t *r, *t;
+       struct dm_raid_superblock *sb2;
+
+       sb = page_address(rdev->sb_page);
+       events_sb = le64_to_cpu(sb->events);
+       failed_devices = le64_to_cpu(sb->failed_devices);
+
+       /*
+        * Initialise to 1 if this is a new superblock.
+        */
+       mddev->events = events_sb ? : 1;
+
+       /*
+        * Reshaping is not currently allowed
+        */
+       if ((le32_to_cpu(sb->level) != mddev->level) ||
+           (le32_to_cpu(sb->layout) != mddev->layout) ||
+           (le32_to_cpu(sb->stripe_sectors) != mddev->chunk_sectors)) {
+               DMERR("Reshaping arrays not yet supported.");
+               return -EINVAL;
+       }
+
+       /* We can only change the number of devices in RAID1 right now */
+       if ((rs->raid_type->level != 1) &&
+           (le32_to_cpu(sb->num_devices) != mddev->raid_disks)) {
+               DMERR("Reshaping arrays not yet supported.");
+               return -EINVAL;
+       }
+
+       if (!(rs->print_flags & (DMPF_SYNC | DMPF_NOSYNC)))
+               mddev->recovery_cp = le64_to_cpu(sb->array_resync_offset);
+
+       /*
+        * During load, we set FirstUse if a new superblock was written.
+        * There are two reasons we might not have a superblock:
+        * 1) The array is brand new - in which case, all of the
+        *    devices must have their In_sync bit set.  Also,
+        *    recovery_cp must be 0, unless forced.
+        * 2) This is a new device being added to an old array
+        *    and the new device needs to be rebuilt - in which
+        *    case the In_sync bit will /not/ be set and
+        *    recovery_cp must be MaxSector.
+        */
+       rdev_for_each(r, t, mddev) {
+               if (!test_bit(In_sync, &r->flags)) {
+                       if (!test_bit(FirstUse, &r->flags))
+                               DMERR("Superblock area of "
+                                     "rebuild device %d should have been "
+                                     "cleared.", r->raid_disk);
+                       set_bit(FirstUse, &r->flags);
+                       rebuilds++;
+               } else if (test_bit(FirstUse, &r->flags))
+                       new_devs++;
+       }
+
+       if (!rebuilds) {
+               if (new_devs == mddev->raid_disks) {
+                       DMINFO("Superblocks created for new array");
+                       set_bit(MD_ARRAY_FIRST_USE, &mddev->flags);
+               } else if (new_devs) {
+                       DMERR("New device injected "
+                             "into existing array without 'rebuild' "
+                             "parameter specified");
+                       return -EINVAL;
+               }
+       } else if (new_devs) {
+               DMERR("'rebuild' devices cannot be "
+                     "injected into an array with other first-time devices");
+               return -EINVAL;
+       } else if (mddev->recovery_cp != MaxSector) {
+               DMERR("'rebuild' specified while array is not in-sync");
+               return -EINVAL;
+       }
+
+       /*
+        * Now we set the Faulty bit for those devices that are
+        * recorded in the superblock as failed.
+        */
+       rdev_for_each(r, t, mddev) {
+               if (!r->sb_page)
+                       continue;
+               sb2 = page_address(r->sb_page);
+               sb2->failed_devices = 0;
+
+               /*
+                * Check for any device re-ordering.
+                */
+               if (!test_bit(FirstUse, &r->flags) && (r->raid_disk >= 0)) {
+                       role = le32_to_cpu(sb2->array_position);
+                       if (role != r->raid_disk) {
+                               if (rs->raid_type->level != 1) {
+                                       rs->ti->error = "Cannot change device "
+                                               "positions in RAID array";
+                                       return -EINVAL;
+                               }
+                               DMINFO("RAID1 device #%d now at position #%d",
+                                      role, r->raid_disk);
+                       }
+
+                       /*
+                        * Partial recovery is performed on
+                        * returning failed devices.
+                        */
+                       if (failed_devices & (1 << role))
+                               set_bit(Faulty, &r->flags);
+               }
+       }
+
+       return 0;
+}
+
+static int super_validate(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+       struct dm_raid_superblock *sb = page_address(rdev->sb_page);
+
+       /*
+        * If mddev->events is not set, we know we have not yet initialized
+        * the array.
+        */
+       if (!mddev->events && super_init_validation(mddev, rdev))
+               return -EINVAL;
+
+       mddev->bitmap_info.offset = 4096 >> 9; /* Enable bitmap creation */
+       rdev->mddev->bitmap_info.default_offset = 4096 >> 9;
+       if (!test_bit(FirstUse, &rdev->flags)) {
+               rdev->recovery_offset = le64_to_cpu(sb->disk_recovery_offset);
+               if (rdev->recovery_offset != MaxSector)
+                       clear_bit(In_sync, &rdev->flags);
+       }
+
+       /*
+        * If a device comes back, set it as not In_sync and no longer faulty.
+        */
+       if (test_bit(Faulty, &rdev->flags)) {
+               clear_bit(Faulty, &rdev->flags);
+               clear_bit(In_sync, &rdev->flags);
+               rdev->saved_raid_disk = rdev->raid_disk;
+               rdev->recovery_offset = 0;
+       }
+
+       clear_bit(FirstUse, &rdev->flags);
+
+       return 0;
+}
+
+/*
+ * Analyse superblocks and select the freshest.
+ */
+static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
+{
+       int ret;
+       mdk_rdev_t *rdev, *freshest, *tmp;
+       mddev_t *mddev = &rs->md;
+
+       freshest = NULL;
+       rdev_for_each(rdev, tmp, mddev) {
+               if (!rdev->meta_bdev)
+                       continue;
+
+               ret = super_load(rdev, freshest);
+
+               switch (ret) {
+               case 1:
+                       freshest = rdev;
+                       break;
+               case 0:
+                       break;
+               default:
+                       ti->error = "Failed to load superblock";
+                       return ret;
+               }
+       }
+
+       if (!freshest)
+               return 0;
+
+       /*
+        * Validation of the freshest device provides the source of
+        * validation for the remaining devices.
+        */
+       ti->error = "Unable to assemble array: Invalid superblocks";
+       if (super_validate(mddev, freshest))
+               return -EINVAL;
+
+       rdev_for_each(rdev, tmp, mddev)
+               if ((rdev != freshest) && super_validate(mddev, rdev))
+                       return -EINVAL;
+
+       return 0;
+}
+
 /*
  * Construct a RAID4/5/6 mapping:
  * Args:
  *     <raid_type> <#raid_params> <raid_params>                \
  *     <#raid_devs> { <meta_dev1> <dev1> .. <meta_devN> <devN> }
  *
- * ** metadata devices are not supported yet, use '-' instead **
- *
  * <raid_params> varies by <raid_type>.  See 'parse_raid_params' for
  * details on possible <raid_params>.
  */
@@ -465,8 +962,12 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
        if (ret)
                goto bad;
 
+       rs->md.sync_super = super_sync;
+       ret = analyse_superblocks(ti, rs);
+       if (ret)
+               goto bad;
+
        INIT_WORK(&rs->md.event_work, do_table_event);
-       ti->split_io = rs->md.chunk_sectors;
        ti->private = rs;
 
        mutex_lock(&rs->md.reconfig_mutex);
@@ -482,6 +983,7 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
        rs->callbacks.congested_fn = raid_is_congested;
        dm_table_add_target_callbacks(ti->table, &rs->callbacks);
 
+       mddev_suspend(&rs->md);
        return 0;
 
 bad:
@@ -546,12 +1048,17 @@ static int raid_status(struct dm_target *ti, status_type_t type,
                break;
        case STATUSTYPE_TABLE:
                /* The string you would use to construct this array */
-               for (i = 0; i < rs->md.raid_disks; i++)
-                       if (rs->dev[i].data_dev &&
+               for (i = 0; i < rs->md.raid_disks; i++) {
+                       if ((rs->print_flags & DMPF_REBUILD) &&
+                           rs->dev[i].data_dev &&
                            !test_bit(In_sync, &rs->dev[i].rdev.flags))
-                               raid_param_cnt++; /* for rebuilds */
+                               raid_param_cnt += 2; /* for rebuilds */
+                       if (rs->dev[i].data_dev &&
+                           test_bit(WriteMostly, &rs->dev[i].rdev.flags))
+                               raid_param_cnt += 2;
+               }
 
-               raid_param_cnt += (hweight64(rs->print_flags) * 2);
+               raid_param_cnt += (hweight64(rs->print_flags & ~DMPF_REBUILD) * 2);
                if (rs->print_flags & (DMPF_SYNC | DMPF_NOSYNC))
                        raid_param_cnt--;
 
@@ -565,7 +1072,8 @@ static int raid_status(struct dm_target *ti, status_type_t type,
                        DMEMIT(" nosync");
 
                for (i = 0; i < rs->md.raid_disks; i++)
-                       if (rs->dev[i].data_dev &&
+                       if ((rs->print_flags & DMPF_REBUILD) &&
+                           rs->dev[i].data_dev &&
                            !test_bit(In_sync, &rs->dev[i].rdev.flags))
                                DMEMIT(" rebuild %u", i);
 
@@ -579,6 +1087,11 @@ static int raid_status(struct dm_target *ti, status_type_t type,
                if (rs->print_flags & DMPF_MAX_RECOVERY_RATE)
                        DMEMIT(" max_recovery_rate %d", rs->md.sync_speed_max);
 
+               for (i = 0; i < rs->md.raid_disks; i++)
+                       if (rs->dev[i].data_dev &&
+                           test_bit(WriteMostly, &rs->dev[i].rdev.flags))
+                               DMEMIT(" write_mostly %u", i);
+
                if (rs->print_flags & DMPF_MAX_WRITE_BEHIND)
                        DMEMIT(" max_write_behind %lu",
                               rs->md.bitmap_info.max_write_behind);
@@ -591,9 +1104,16 @@ static int raid_status(struct dm_target *ti, status_type_t type,
                               conf ? conf->max_nr_stripes * 2 : 0);
                }
 
+               if (rs->print_flags & DMPF_REGION_SIZE)
+                       DMEMIT(" region_size %lu",
+                              rs->md.bitmap_info.chunksize >> 9);
+
                DMEMIT(" %d", rs->md.raid_disks);
                for (i = 0; i < rs->md.raid_disks; i++) {
-                       DMEMIT(" -"); /* metadata device */
+                       if (rs->dev[i].meta_dev)
+                               DMEMIT(" %s", rs->dev[i].meta_dev->name);
+                       else
+                               DMEMIT(" -");
 
                        if (rs->dev[i].data_dev)
                                DMEMIT(" %s", rs->dev[i].data_dev->name);
@@ -650,12 +1170,13 @@ static void raid_resume(struct dm_target *ti)
 {
        struct raid_set *rs = ti->private;
 
+       bitmap_load(&rs->md);
        mddev_resume(&rs->md);
 }
 
 static struct target_type raid_target = {
        .name = "raid",
-       .version = {1, 0, 0},
+       .version = {1, 1, 0},
        .module = THIS_MODULE,
        .ctr = raid_ctr,
        .dtr = raid_dtr,
index 135c2f1fdbfcc95aefb0d842030e71a089e87d30..d1f1d70171038cf9836832b68c63ad4b1d15297a 100644 (file)
 #define NUM_SNAPSHOT_HDR_CHUNKS 1
 
 struct disk_header {
-       uint32_t magic;
+       __le32 magic;
 
        /*
         * Is this snapshot valid.  There is no way of recovering
         * an invalid snapshot.
         */
-       uint32_t valid;
+       __le32 valid;
 
        /*
         * Simple, incrementing version. no backward
         * compatibility.
         */
-       uint32_t version;
+       __le32 version;
 
        /* In sectors */
-       uint32_t chunk_size;
-};
+       __le32 chunk_size;
+} __packed;
 
 struct disk_exception {
+       __le64 old_chunk;
+       __le64 new_chunk;
+} __packed;
+
+struct core_exception {
        uint64_t old_chunk;
        uint64_t new_chunk;
 };
@@ -169,10 +174,9 @@ static int alloc_area(struct pstore *ps)
        if (!ps->area)
                goto err_area;
 
-       ps->zero_area = vmalloc(len);
+       ps->zero_area = vzalloc(len);
        if (!ps->zero_area)
                goto err_zero_area;
-       memset(ps->zero_area, 0, len);
 
        ps->header_area = vmalloc(len);
        if (!ps->header_area)
@@ -396,32 +400,32 @@ static struct disk_exception *get_exception(struct pstore *ps, uint32_t index)
 }
 
 static void read_exception(struct pstore *ps,
-                          uint32_t index, struct disk_exception *result)
+                          uint32_t index, struct core_exception *result)
 {
-       struct disk_exception *e = get_exception(ps, index);
+       struct disk_exception *de = get_exception(ps, index);
 
        /* copy it */
-       result->old_chunk = le64_to_cpu(e->old_chunk);
-       result->new_chunk = le64_to_cpu(e->new_chunk);
+       result->old_chunk = le64_to_cpu(de->old_chunk);
+       result->new_chunk = le64_to_cpu(de->new_chunk);
 }
 
 static void write_exception(struct pstore *ps,
-                           uint32_t index, struct disk_exception *de)
+                           uint32_t index, struct core_exception *e)
 {
-       struct disk_exception *e = get_exception(ps, index);
+       struct disk_exception *de = get_exception(ps, index);
 
        /* copy it */
-       e->old_chunk = cpu_to_le64(de->old_chunk);
-       e->new_chunk = cpu_to_le64(de->new_chunk);
+       de->old_chunk = cpu_to_le64(e->old_chunk);
+       de->new_chunk = cpu_to_le64(e->new_chunk);
 }
 
 static void clear_exception(struct pstore *ps, uint32_t index)
 {
-       struct disk_exception *e = get_exception(ps, index);
+       struct disk_exception *de = get_exception(ps, index);
 
        /* clear it */
-       e->old_chunk = 0;
-       e->new_chunk = 0;
+       de->old_chunk = 0;
+       de->new_chunk = 0;
 }
 
 /*
@@ -437,13 +441,13 @@ static int insert_exceptions(struct pstore *ps,
 {
        int r;
        unsigned int i;
-       struct disk_exception de;
+       struct core_exception e;
 
        /* presume the area is full */
        *full = 1;
 
        for (i = 0; i < ps->exceptions_per_area; i++) {
-               read_exception(ps, i, &de);
+               read_exception(ps, i, &e);
 
                /*
                 * If the new_chunk is pointing at the start of
@@ -451,7 +455,7 @@ static int insert_exceptions(struct pstore *ps,
                 * is we know that we've hit the end of the
                 * exceptions.  Therefore the area is not full.
                 */
-               if (de.new_chunk == 0LL) {
+               if (e.new_chunk == 0LL) {
                        ps->current_committed = i;
                        *full = 0;
                        break;
@@ -460,13 +464,13 @@ static int insert_exceptions(struct pstore *ps,
                /*
                 * Keep track of the start of the free chunks.
                 */
-               if (ps->next_free <= de.new_chunk)
-                       ps->next_free = de.new_chunk + 1;
+               if (ps->next_free <= e.new_chunk)
+                       ps->next_free = e.new_chunk + 1;
 
                /*
                 * Otherwise we add the exception to the snapshot.
                 */
-               r = callback(callback_context, de.old_chunk, de.new_chunk);
+               r = callback(callback_context, e.old_chunk, e.new_chunk);
                if (r)
                        return r;
        }
@@ -563,7 +567,7 @@ static int persistent_read_metadata(struct dm_exception_store *store,
        ps->exceptions_per_area = (ps->store->chunk_size << SECTOR_SHIFT) /
                                  sizeof(struct disk_exception);
        ps->callbacks = dm_vcalloc(ps->exceptions_per_area,
-                       sizeof(*ps->callbacks));
+                                  sizeof(*ps->callbacks));
        if (!ps->callbacks)
                return -ENOMEM;
 
@@ -641,12 +645,12 @@ static void persistent_commit_exception(struct dm_exception_store *store,
 {
        unsigned int i;
        struct pstore *ps = get_info(store);
-       struct disk_exception de;
+       struct core_exception ce;
        struct commit_callback *cb;
 
-       de.old_chunk = e->old_chunk;
-       de.new_chunk = e->new_chunk;
-       write_exception(ps, ps->current_committed++, &de);
+       ce.old_chunk = e->old_chunk;
+       ce.new_chunk = e->new_chunk;
+       write_exception(ps, ps->current_committed++, &ce);
 
        /*
         * Add the callback to the back of the array.  This code
@@ -670,7 +674,7 @@ static void persistent_commit_exception(struct dm_exception_store *store,
         * If we completely filled the current area, then wipe the next one.
         */
        if ((ps->current_committed == ps->exceptions_per_area) &&
-            zero_disk_area(ps, ps->current_area + 1))
+           zero_disk_area(ps, ps->current_area + 1))
                ps->valid = 0;
 
        /*
@@ -701,7 +705,7 @@ static int persistent_prepare_merge(struct dm_exception_store *store,
                                    chunk_t *last_new_chunk)
 {
        struct pstore *ps = get_info(store);
-       struct disk_exception de;
+       struct core_exception ce;
        int nr_consecutive;
        int r;
 
@@ -722,9 +726,9 @@ static int persistent_prepare_merge(struct dm_exception_store *store,
                ps->current_committed = ps->exceptions_per_area;
        }
 
-       read_exception(ps, ps->current_committed - 1, &de);
-       *last_old_chunk = de.old_chunk;
-       *last_new_chunk = de.new_chunk;
+       read_exception(ps, ps->current_committed - 1, &ce);
+       *last_old_chunk = ce.old_chunk;
+       *last_new_chunk = ce.new_chunk;
 
        /*
         * Find number of consecutive chunks within the current area,
@@ -733,9 +737,9 @@ static int persistent_prepare_merge(struct dm_exception_store *store,
        for (nr_consecutive = 1; nr_consecutive < ps->current_committed;
             nr_consecutive++) {
                read_exception(ps, ps->current_committed - 1 - nr_consecutive,
-                              &de);
-               if (de.old_chunk != *last_old_chunk - nr_consecutive ||
-                   de.new_chunk != *last_new_chunk - nr_consecutive)
+                              &ce);
+               if (ce.old_chunk != *last_old_chunk - nr_consecutive ||
+                   ce.new_chunk != *last_new_chunk - nr_consecutive)
                        break;
        }
 
@@ -753,7 +757,7 @@ static int persistent_commit_merge(struct dm_exception_store *store,
        for (i = 0; i < nr_merged; i++)
                clear_exception(ps, ps->current_committed - 1 - i);
 
-       r = area_io(ps, WRITE);
+       r = area_io(ps, WRITE_FLUSH_FUA);
        if (r < 0)
                return r;
 
index 9ecff5f3023a4c4f0721b958ef1541e762e910ff..6f758870fc19cf0e66db3055dab36ab96e038fee 100644 (file)
@@ -29,16 +29,6 @@ static const char dm_snapshot_merge_target_name[] = "snapshot-merge";
 #define dm_target_is_snapshot_merge(ti) \
        ((ti)->type->name == dm_snapshot_merge_target_name)
 
-/*
- * The percentage increment we will wake up users at
- */
-#define WAKE_UP_PERCENT 5
-
-/*
- * kcopyd priority of snapshot operations
- */
-#define SNAPSHOT_COPY_PRIORITY 2
-
 /*
  * The size of the mempool used to track chunks in use.
  */
@@ -180,6 +170,13 @@ struct dm_snap_pending_exception {
         * kcopyd.
         */
        int started;
+
+       /*
+        * For writing a complete chunk, bypassing the copy.
+        */
+       struct bio *full_bio;
+       bio_end_io_t *full_bio_end_io;
+       void *full_bio_private;
 };
 
 /*
@@ -1055,8 +1052,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 
        s = kmalloc(sizeof(*s), GFP_KERNEL);
        if (!s) {
-               ti->error = "Cannot allocate snapshot context private "
-                   "structure";
+               ti->error = "Cannot allocate private snapshot structure";
                r = -ENOMEM;
                goto bad;
        }
@@ -1380,6 +1376,7 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
        struct dm_snapshot *s = pe->snap;
        struct bio *origin_bios = NULL;
        struct bio *snapshot_bios = NULL;
+       struct bio *full_bio = NULL;
        int error = 0;
 
        if (!success) {
@@ -1415,10 +1412,15 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
         */
        dm_insert_exception(&s->complete, e);
 
- out:
+out:
        dm_remove_exception(&pe->e);
        snapshot_bios = bio_list_get(&pe->snapshot_bios);
        origin_bios = bio_list_get(&pe->origin_bios);
+       full_bio = pe->full_bio;
+       if (full_bio) {
+               full_bio->bi_end_io = pe->full_bio_end_io;
+               full_bio->bi_private = pe->full_bio_private;
+       }
        free_pending_exception(pe);
 
        increment_pending_exceptions_done_count();
@@ -1426,10 +1428,15 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
        up_write(&s->lock);
 
        /* Submit any pending write bios */
-       if (error)
+       if (error) {
+               if (full_bio)
+                       bio_io_error(full_bio);
                error_bios(snapshot_bios);
-       else
+       } else {
+               if (full_bio)
+                       bio_endio(full_bio, 0);
                flush_bios(snapshot_bios);
+       }
 
        retry_origin_bios(s, origin_bios);
 }
@@ -1480,8 +1487,33 @@ static void start_copy(struct dm_snap_pending_exception *pe)
        dest.count = src.count;
 
        /* Hand over to kcopyd */
-       dm_kcopyd_copy(s->kcopyd_client,
-                   &src, 1, &dest, 0, copy_callback, pe);
+       dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, copy_callback, pe);
+}
+
+static void full_bio_end_io(struct bio *bio, int error)
+{
+       void *callback_data = bio->bi_private;
+
+       dm_kcopyd_do_callback(callback_data, 0, error ? 1 : 0);
+}
+
+static void start_full_bio(struct dm_snap_pending_exception *pe,
+                          struct bio *bio)
+{
+       struct dm_snapshot *s = pe->snap;
+       void *callback_data;
+
+       pe->full_bio = bio;
+       pe->full_bio_end_io = bio->bi_end_io;
+       pe->full_bio_private = bio->bi_private;
+
+       callback_data = dm_kcopyd_prepare_callback(s->kcopyd_client,
+                                                  copy_callback, pe);
+
+       bio->bi_end_io = full_bio_end_io;
+       bio->bi_private = callback_data;
+
+       generic_make_request(bio);
 }
 
 static struct dm_snap_pending_exception *
@@ -1519,6 +1551,7 @@ __find_pending_exception(struct dm_snapshot *s,
        bio_list_init(&pe->origin_bios);
        bio_list_init(&pe->snapshot_bios);
        pe->started = 0;
+       pe->full_bio = NULL;
 
        if (s->store->type->prepare_exception(s->store, &pe->e)) {
                free_pending_exception(pe);
@@ -1612,10 +1645,19 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
                }
 
                remap_exception(s, &pe->e, bio, chunk);
-               bio_list_add(&pe->snapshot_bios, bio);
 
                r = DM_MAPIO_SUBMITTED;
 
+               if (!pe->started &&
+                   bio->bi_size == (s->store->chunk_size << SECTOR_SHIFT)) {
+                       pe->started = 1;
+                       up_write(&s->lock);
+                       start_full_bio(pe, bio);
+                       goto out;
+               }
+
+               bio_list_add(&pe->snapshot_bios, bio);
+
                if (!pe->started) {
                        /* this is protected by snap->lock */
                        pe->started = 1;
@@ -1628,9 +1670,9 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
                map_context->ptr = track_chunk(s, chunk);
        }
 
- out_unlock:
+out_unlock:
        up_write(&s->lock);
- out:
+out:
        return r;
 }
 
@@ -1974,7 +2016,7 @@ static int __origin_write(struct list_head *snapshots, sector_t sector,
                        pe_to_start_now = pe;
                }
 
- next_snapshot:
+next_snapshot:
                up_write(&snap->lock);
 
                if (pe_to_start_now) {
index bfe9c2333ceacecddd00d058615fc8a9f2867b1b..986b8754bb0813c59fb22a0feae7083740378f72 100644 (file)
@@ -54,7 +54,6 @@ struct dm_table {
        sector_t *highs;
        struct dm_target *targets;
 
-       unsigned discards_supported:1;
        unsigned integrity_supported:1;
 
        /*
@@ -154,12 +153,11 @@ void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size)
                return NULL;
 
        size = nmemb * elem_size;
-       addr = vmalloc(size);
-       if (addr)
-               memset(addr, 0, size);
+       addr = vzalloc(size);
 
        return addr;
 }
+EXPORT_SYMBOL(dm_vcalloc);
 
 /*
  * highs, and targets are managed as dynamic arrays during a
@@ -209,7 +207,6 @@ int dm_table_create(struct dm_table **result, fmode_t mode,
        INIT_LIST_HEAD(&t->devices);
        INIT_LIST_HEAD(&t->target_callbacks);
        atomic_set(&t->holders, 0);
-       t->discards_supported = 1;
 
        if (!num_targets)
                num_targets = KEYS_PER_NODE;
@@ -281,6 +278,7 @@ void dm_table_get(struct dm_table *t)
 {
        atomic_inc(&t->holders);
 }
+EXPORT_SYMBOL(dm_table_get);
 
 void dm_table_put(struct dm_table *t)
 {
@@ -290,6 +288,7 @@ void dm_table_put(struct dm_table *t)
        smp_mb__before_atomic_dec();
        atomic_dec(&t->holders);
 }
+EXPORT_SYMBOL(dm_table_put);
 
 /*
  * Checks to see if we need to extend highs or targets.
@@ -455,13 +454,14 @@ static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode,
  * Add a device to the list, or just increment the usage count if
  * it's already present.
  */
-static int __table_get_device(struct dm_table *t, struct dm_target *ti,
-                     const char *path, 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)
 {
        int r;
        dev_t uninitialized_var(dev);
        struct dm_dev_internal *dd;
        unsigned int major, minor;
+       struct dm_table *t = ti->table;
 
        BUG_ON(!t);
 
@@ -509,6 +509,7 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti,
        *result = &dd->dm_dev;
        return 0;
 }
+EXPORT_SYMBOL(dm_get_device);
 
 int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
                         sector_t start, sector_t len, void *data)
@@ -539,23 +540,15 @@ int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
         * If not we'll force DM to use PAGE_SIZE or
         * smaller I/O, just to be safe.
         */
-
-       if (q->merge_bvec_fn && !ti->type->merge)
+       if (dm_queue_merge_is_compulsory(q) && !ti->type->merge)
                blk_limits_max_hw_sectors(limits,
                                          (unsigned int) (PAGE_SIZE >> 9));
        return 0;
 }
 EXPORT_SYMBOL_GPL(dm_set_device_limits);
 
-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, mode, result);
-}
-
-
 /*
- * Decrement a devices use count and remove it if necessary.
+ * Decrement a device's use count and remove it if necessary.
  */
 void dm_put_device(struct dm_target *ti, struct dm_dev *d)
 {
@@ -568,6 +561,7 @@ void dm_put_device(struct dm_target *ti, struct dm_dev *d)
                kfree(dd);
        }
 }
+EXPORT_SYMBOL(dm_put_device);
 
 /*
  * Checks to see if the target joins onto the end of the table.
@@ -791,8 +785,9 @@ int dm_table_add_target(struct dm_table *t, const char *type,
 
        t->highs[t->num_targets++] = tgt->begin + tgt->len - 1;
 
-       if (!tgt->num_discard_requests)
-               t->discards_supported = 0;
+       if (!tgt->num_discard_requests && tgt->discards_supported)
+               DMWARN("%s: %s: ignoring discards_supported because num_discard_requests is zero.",
+                      dm_device_name(t->md), type);
 
        return 0;
 
@@ -802,6 +797,63 @@ int dm_table_add_target(struct dm_table *t, const char *type,
        return r;
 }
 
+/*
+ * Target argument parsing helpers.
+ */
+static int validate_next_arg(struct dm_arg *arg, struct dm_arg_set *arg_set,
+                            unsigned *value, char **error, unsigned grouped)
+{
+       const char *arg_str = dm_shift_arg(arg_set);
+
+       if (!arg_str ||
+           (sscanf(arg_str, "%u", value) != 1) ||
+           (*value < arg->min) ||
+           (*value > arg->max) ||
+           (grouped && arg_set->argc < *value)) {
+               *error = arg->error;
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int dm_read_arg(struct dm_arg *arg, struct dm_arg_set *arg_set,
+               unsigned *value, char **error)
+{
+       return validate_next_arg(arg, arg_set, value, error, 0);
+}
+EXPORT_SYMBOL(dm_read_arg);
+
+int dm_read_arg_group(struct dm_arg *arg, struct dm_arg_set *arg_set,
+                     unsigned *value, char **error)
+{
+       return validate_next_arg(arg, arg_set, value, error, 1);
+}
+EXPORT_SYMBOL(dm_read_arg_group);
+
+const char *dm_shift_arg(struct dm_arg_set *as)
+{
+       char *r;
+
+       if (as->argc) {
+               as->argc--;
+               r = *as->argv;
+               as->argv++;
+               return r;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL(dm_shift_arg);
+
+void dm_consume_args(struct dm_arg_set *as, unsigned num_args)
+{
+       BUG_ON(as->argc < num_args);
+       as->argc -= num_args;
+       as->argv += num_args;
+}
+EXPORT_SYMBOL(dm_consume_args);
+
 static int dm_table_set_type(struct dm_table *t)
 {
        unsigned i;
@@ -1077,11 +1129,13 @@ void dm_table_event(struct dm_table *t)
                t->event_fn(t->event_context);
        mutex_unlock(&_event_lock);
 }
+EXPORT_SYMBOL(dm_table_event);
 
 sector_t dm_table_get_size(struct dm_table *t)
 {
        return t->num_targets ? (t->highs[t->num_targets - 1] + 1) : 0;
 }
+EXPORT_SYMBOL(dm_table_get_size);
 
 struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index)
 {
@@ -1194,9 +1248,45 @@ static void dm_table_set_integrity(struct dm_table *t)
                               blk_get_integrity(template_disk));
 }
 
+static int device_flush_capable(struct dm_target *ti, struct dm_dev *dev,
+                               sector_t start, sector_t len, void *data)
+{
+       unsigned flush = (*(unsigned *)data);
+       struct request_queue *q = bdev_get_queue(dev->bdev);
+
+       return q && (q->flush_flags & flush);
+}
+
+static bool dm_table_supports_flush(struct dm_table *t, unsigned flush)
+{
+       struct dm_target *ti;
+       unsigned i = 0;
+
+       /*
+        * Require at least one underlying device to support flushes.
+        * t->devices includes internal dm devices such as mirror logs
+        * so we need to use iterate_devices here, which targets
+        * supporting flushes must provide.
+        */
+       while (i < dm_table_get_num_targets(t)) {
+               ti = dm_table_get_target(t, i++);
+
+               if (!ti->num_flush_requests)
+                       continue;
+
+               if (ti->type->iterate_devices &&
+                   ti->type->iterate_devices(ti, device_flush_capable, &flush))
+                       return 1;
+       }
+
+       return 0;
+}
+
 void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
                               struct queue_limits *limits)
 {
+       unsigned flush = 0;
+
        /*
         * Copy table's limits to the DM device's request_queue
         */
@@ -1207,6 +1297,13 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
        else
                queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
 
+       if (dm_table_supports_flush(t, REQ_FLUSH)) {
+               flush |= REQ_FLUSH;
+               if (dm_table_supports_flush(t, REQ_FUA))
+                       flush |= REQ_FUA;
+       }
+       blk_queue_flush(q, flush);
+
        dm_table_set_integrity(t);
 
        /*
@@ -1237,6 +1334,7 @@ fmode_t dm_table_get_mode(struct dm_table *t)
 {
        return t->mode;
 }
+EXPORT_SYMBOL(dm_table_get_mode);
 
 static void suspend_targets(struct dm_table *t, unsigned postsuspend)
 {
@@ -1345,6 +1443,7 @@ struct mapped_device *dm_table_get_md(struct dm_table *t)
 {
        return t->md;
 }
+EXPORT_SYMBOL(dm_table_get_md);
 
 static int device_discard_capable(struct dm_target *ti, struct dm_dev *dev,
                                  sector_t start, sector_t len, void *data)
@@ -1359,19 +1458,19 @@ bool dm_table_supports_discards(struct dm_table *t)
        struct dm_target *ti;
        unsigned i = 0;
 
-       if (!t->discards_supported)
-               return 0;
-
        /*
         * Unless any target used by the table set discards_supported,
         * require at least one underlying device to support discards.
         * t->devices includes internal dm devices such as mirror logs
         * so we need to use iterate_devices here, which targets
-        * supporting discard must provide.
+        * supporting discard selectively must provide.
         */
        while (i < dm_table_get_num_targets(t)) {
                ti = dm_table_get_target(t, i++);
 
+               if (!ti->num_discard_requests)
+                       continue;
+
                if (ti->discards_supported)
                        return 1;
 
@@ -1382,13 +1481,3 @@ bool dm_table_supports_discards(struct dm_table *t)
 
        return 0;
 }
-
-EXPORT_SYMBOL(dm_vcalloc);
-EXPORT_SYMBOL(dm_get_device);
-EXPORT_SYMBOL(dm_put_device);
-EXPORT_SYMBOL(dm_table_event);
-EXPORT_SYMBOL(dm_table_get_size);
-EXPORT_SYMBOL(dm_table_get_mode);
-EXPORT_SYMBOL(dm_table_get_md);
-EXPORT_SYMBOL(dm_table_put);
-EXPORT_SYMBOL(dm_table_get);
index 0cf68b478878f327598fb756fe2da173d5443468..52b39f335bb38549045f46eae8cad3714c867c5d 100644 (file)
@@ -37,6 +37,8 @@ static const char *_name = DM_NAME;
 static unsigned int major = 0;
 static unsigned int _major = 0;
 
+static DEFINE_IDR(_minor_idr);
+
 static DEFINE_SPINLOCK(_minor_lock);
 /*
  * For bio-based dm.
@@ -109,6 +111,7 @@ EXPORT_SYMBOL_GPL(dm_get_rq_mapinfo);
 #define DMF_FREEING 3
 #define DMF_DELETING 4
 #define DMF_NOFLUSH_SUSPENDING 5
+#define DMF_MERGE_IS_OPTIONAL 6
 
 /*
  * Work processed by per-device workqueue.
@@ -313,6 +316,12 @@ static void __exit dm_exit(void)
 
        while (i--)
                _exits[i]();
+
+       /*
+        * Should be empty by this point.
+        */
+       idr_remove_all(&_minor_idr);
+       idr_destroy(&_minor_idr);
 }
 
 /*
@@ -1171,7 +1180,8 @@ static int __clone_and_map_discard(struct clone_info *ci)
 
                /*
                 * Even though the device advertised discard support,
-                * reconfiguration might have changed that since the
+                * that does not mean every target supports it, and
+                * reconfiguration might also have changed that since the
                 * check was performed.
                 */
                if (!ti->num_discard_requests)
@@ -1705,8 +1715,6 @@ static int dm_any_congested(void *congested_data, int bdi_bits)
 /*-----------------------------------------------------------------
  * An IDR is used to keep track of allocated minor numbers.
  *---------------------------------------------------------------*/
-static DEFINE_IDR(_minor_idr);
-
 static void free_minor(int minor)
 {
        spin_lock(&_minor_lock);
@@ -1800,7 +1808,6 @@ static void dm_init_md_queue(struct mapped_device *md)
        blk_queue_make_request(md->queue, dm_request);
        blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY);
        blk_queue_merge_bvec(md->queue, dm_merge_bvec);
-       blk_queue_flush(md->queue, REQ_FLUSH | REQ_FUA);
 }
 
 /*
@@ -1985,6 +1992,59 @@ static void __set_size(struct mapped_device *md, sector_t size)
        i_size_write(md->bdev->bd_inode, (loff_t)size << SECTOR_SHIFT);
 }
 
+/*
+ * Return 1 if the queue has a compulsory merge_bvec_fn function.
+ *
+ * If this function returns 0, then the device is either a non-dm
+ * device without a merge_bvec_fn, or it is a dm device that is
+ * able to split any bios it receives that are too big.
+ */
+int dm_queue_merge_is_compulsory(struct request_queue *q)
+{
+       struct mapped_device *dev_md;
+
+       if (!q->merge_bvec_fn)
+               return 0;
+
+       if (q->make_request_fn == dm_request) {
+               dev_md = q->queuedata;
+               if (test_bit(DMF_MERGE_IS_OPTIONAL, &dev_md->flags))
+                       return 0;
+       }
+
+       return 1;
+}
+
+static int dm_device_merge_is_compulsory(struct dm_target *ti,
+                                        struct dm_dev *dev, sector_t start,
+                                        sector_t len, void *data)
+{
+       struct block_device *bdev = dev->bdev;
+       struct request_queue *q = bdev_get_queue(bdev);
+
+       return dm_queue_merge_is_compulsory(q);
+}
+
+/*
+ * Return 1 if it is acceptable to ignore merge_bvec_fn based
+ * on the properties of the underlying devices.
+ */
+static int dm_table_merge_is_optional(struct dm_table *table)
+{
+       unsigned i = 0;
+       struct dm_target *ti;
+
+       while (i < dm_table_get_num_targets(table)) {
+               ti = dm_table_get_target(table, i++);
+
+               if (ti->type->iterate_devices &&
+                   ti->type->iterate_devices(ti, dm_device_merge_is_compulsory, NULL))
+                       return 0;
+       }
+
+       return 1;
+}
+
 /*
  * Returns old map, which caller must destroy.
  */
@@ -1995,6 +2055,7 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
        struct request_queue *q = md->queue;
        sector_t size;
        unsigned long flags;
+       int merge_is_optional;
 
        size = dm_table_get_size(t);
 
@@ -2020,10 +2081,16 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
 
        __bind_mempools(md, t);
 
+       merge_is_optional = dm_table_merge_is_optional(t);
+
        write_lock_irqsave(&md->map_lock, flags);
        old_map = md->map;
        md->map = t;
        dm_table_set_restrictions(t, q, limits);
+       if (merge_is_optional)
+               set_bit(DMF_MERGE_IS_OPTIONAL, &md->flags);
+       else
+               clear_bit(DMF_MERGE_IS_OPTIONAL, &md->flags);
        write_unlock_irqrestore(&md->map_lock, flags);
 
        return old_map;
index 1aaf16746da86f6bfb9c9ffaab334841c044d92a..6745dbd278a4ffc463188c72cab4f301a74adc74 100644 (file)
@@ -66,6 +66,8 @@ int dm_table_alloc_md_mempools(struct dm_table *t);
 void dm_table_free_md_mempools(struct dm_table *t);
 struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t);
 
+int dm_queue_merge_is_compulsory(struct request_queue *q);
+
 void dm_lock_md_type(struct mapped_device *md);
 void dm_unlock_md_type(struct mapped_device *md);
 void dm_set_md_type(struct mapped_device *md, unsigned type);
index 5b0dba6d4efa6d00b3a93d90640baacb7ef5a8e8..d724a18b5285dd13e9caffdbd14f907fd81b5d08 100644 (file)
@@ -1989,14 +1989,20 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode)
                return -EINVAL;
        }
 
+       /*
+        * It's important to set the bp->state to the value different from
+        * BNX2X_STATE_OPEN and only then stop the Tx. Otherwise bnx2x_tx_int()
+        * may restart the Tx from the NAPI context (see bnx2x_tx_int()).
+        */
+       bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT;
+       smp_mb();
+
        /* Stop Tx */
        bnx2x_tx_disable(bp);
 
 #ifdef BCM_CNIC
        bnx2x_cnic_notify(bp, CNIC_CTL_STOP_CMD);
 #endif
-       bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT;
-       smp_mb();
 
        bp->rx_mode = BNX2X_RX_MODE_NONE;
 
index 06727f32e5050a9c43a09a2363008f1c02045f17..dc24de40e33608b239dbfd455319efab44c82d4a 100644 (file)
@@ -1204,6 +1204,8 @@ struct drv_port_mb {
 
        #define LINK_STATUS_PFC_ENABLED                         0x20000000
 
+       #define LINK_STATUS_PHYSICAL_LINK_FLAG                  0x40000000
+
        u32 port_stx;
 
        u32 stat_nig_timer;
index bcd8f00386287d46c3ebacb2fce644f355ba8f88..d45b1555a6022a39d10270ed53393755b382af3a 100644 (file)
@@ -1546,6 +1546,12 @@ static void bnx2x_umac_enable(struct link_params *params,
                               vars->line_speed);
                break;
        }
+       if (!(vars->flow_ctrl & BNX2X_FLOW_CTRL_TX))
+               val |= UMAC_COMMAND_CONFIG_REG_IGNORE_TX_PAUSE;
+
+       if (!(vars->flow_ctrl & BNX2X_FLOW_CTRL_RX))
+               val |= UMAC_COMMAND_CONFIG_REG_PAUSE_IGNORE;
+
        REG_WR(bp, umac_base + UMAC_REG_COMMAND_CONFIG, val);
        udelay(50);
 
@@ -1661,10 +1667,20 @@ static void bnx2x_xmac_disable(struct link_params *params)
 {
        u8 port = params->port;
        struct bnx2x *bp = params->bp;
-       u32 xmac_base = (port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
+       u32 pfc_ctrl, xmac_base = (port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
 
        if (REG_RD(bp, MISC_REG_RESET_REG_2) &
            MISC_REGISTERS_RESET_REG_2_XMAC) {
+               /*
+                * Send an indication to change the state in the NIG back to XON
+                * Clearing this bit enables the next set of this bit to get
+                * rising edge
+                */
+               pfc_ctrl = REG_RD(bp, xmac_base + XMAC_REG_PFC_CTRL_HI);
+               REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL_HI,
+                      (pfc_ctrl & ~(1<<1)));
+               REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL_HI,
+                      (pfc_ctrl | (1<<1)));
                DP(NETIF_MSG_LINK, "Disable XMAC on port %x\n", port);
                REG_WR(bp, xmac_base + XMAC_REG_CTRL, 0);
                usleep_range(1000, 1000);
@@ -1729,6 +1745,10 @@ static int bnx2x_emac_enable(struct link_params *params,
 
        DP(NETIF_MSG_LINK, "enabling EMAC\n");
 
+       /* Disable BMAC */
+       REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
+              (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
+
        /* enable emac and not bmac */
        REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT + port*4, 1);
 
@@ -2583,12 +2603,6 @@ static int bnx2x_bmac1_enable(struct link_params *params,
        REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_LLFC_MSG_FLDS,
                    wb_data, 2);
 
-       if (vars->phy_flags & PHY_TX_ERROR_CHECK_FLAG) {
-               REG_RD_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_LSS_STATUS,
-                           wb_data, 2);
-               if (wb_data[0] > 0)
-                       return -ESRCH;
-       }
        return 0;
 }
 
@@ -2654,16 +2668,6 @@ static int bnx2x_bmac2_enable(struct link_params *params,
        udelay(30);
        bnx2x_update_pfc_bmac2(params, vars, is_lb);
 
-       if (vars->phy_flags & PHY_TX_ERROR_CHECK_FLAG) {
-               REG_RD_DMAE(bp, bmac_addr + BIGMAC2_REGISTER_RX_LSS_STAT,
-                           wb_data, 2);
-               if (wb_data[0] > 0) {
-                       DP(NETIF_MSG_LINK, "Got bad LSS status 0x%x\n",
-                                      wb_data[0]);
-                       return -ESRCH;
-               }
-       }
-
        return 0;
 }
 
@@ -2949,7 +2953,9 @@ static int bnx2x_cl45_read(struct bnx2x *bp, struct bnx2x_phy *phy,
        u32 val;
        u16 i;
        int rc = 0;
-
+       if (phy->flags & FLAGS_MDC_MDIO_WA_B0)
+               bnx2x_bits_en(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS,
+                             EMAC_MDIO_STATUS_10MB);
        /* address */
        val = ((phy->addr << 21) | (devad << 16) | reg |
               EMAC_MDIO_COMM_COMMAND_ADDRESS |
@@ -3003,6 +3009,9 @@ static int bnx2x_cl45_read(struct bnx2x *bp, struct bnx2x_phy *phy,
                }
        }
 
+       if (phy->flags & FLAGS_MDC_MDIO_WA_B0)
+               bnx2x_bits_dis(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS,
+                              EMAC_MDIO_STATUS_10MB);
        return rc;
 }
 
@@ -3012,6 +3021,9 @@ static int bnx2x_cl45_write(struct bnx2x *bp, struct bnx2x_phy *phy,
        u32 tmp;
        u8 i;
        int rc = 0;
+       if (phy->flags & FLAGS_MDC_MDIO_WA_B0)
+               bnx2x_bits_en(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS,
+                             EMAC_MDIO_STATUS_10MB);
 
        /* address */
 
@@ -3065,7 +3077,9 @@ static int bnx2x_cl45_write(struct bnx2x *bp, struct bnx2x_phy *phy,
                        bnx2x_cl45_read(bp, phy, devad, 0xf, &temp_val);
                }
        }
-
+       if (phy->flags & FLAGS_MDC_MDIO_WA_B0)
+               bnx2x_bits_dis(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS,
+                              EMAC_MDIO_STATUS_10MB);
        return rc;
 }
 
@@ -4353,6 +4367,9 @@ void bnx2x_link_status_update(struct link_params *params,
 
        vars->link_up = (vars->link_status & LINK_STATUS_LINK_UP);
        vars->phy_flags = PHY_XGXS_FLAG;
+       if (vars->link_status & LINK_STATUS_PHYSICAL_LINK_FLAG)
+               vars->phy_flags |= PHY_PHYSICAL_LINK_FLAG;
+
        if (vars->link_up) {
                DP(NETIF_MSG_LINK, "phy link up\n");
 
@@ -4444,6 +4461,8 @@ void bnx2x_link_status_update(struct link_params *params,
 
                /* indicate no mac active */
                vars->mac_type = MAC_TYPE_NONE;
+               if (vars->link_status & LINK_STATUS_PHYSICAL_LINK_FLAG)
+                       vars->phy_flags |= PHY_HALF_OPEN_CONN_FLAG;
        }
 
        /* Sync media type */
@@ -5903,20 +5922,30 @@ int bnx2x_set_led(struct link_params *params,
                                tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED);
                                EMAC_WR(bp, EMAC_REG_EMAC_LED,
                                        (tmp | EMAC_LED_OVERRIDE));
-                               return rc;
+                               /*
+                                * return here without enabling traffic
+                                * LED blink andsetting rate in ON mode.
+                                * In oper mode, enabling LED blink
+                                * and setting rate is needed.
+                                */
+                               if (mode == LED_MODE_ON)
+                                       return rc;
                        }
-               } else if (SINGLE_MEDIA_DIRECT(params) &&
-                          (CHIP_IS_E1x(bp) ||
-                           CHIP_IS_E2(bp))) {
+               } else if (SINGLE_MEDIA_DIRECT(params)) {
                        /*
                         * This is a work-around for HW issue found when link
                         * is up in CL73
                         */
-                       REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, 0);
                        REG_WR(bp, NIG_REG_LED_10G_P0 + port*4, 1);
-               } else {
+                       if (CHIP_IS_E1x(bp) ||
+                           CHIP_IS_E2(bp) ||
+                           (mode == LED_MODE_ON))
+                               REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, 0);
+                       else
+                               REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4,
+                                      hw_led_mode);
+               } else
                        REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, hw_led_mode);
-               }
 
                REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 0);
                /* Set blinking rate to ~15.9Hz */
@@ -6160,6 +6189,7 @@ static int bnx2x_update_link_down(struct link_params *params,
        /* update shared memory */
        vars->link_status &= ~(LINK_STATUS_SPEED_AND_DUPLEX_MASK |
                               LINK_STATUS_LINK_UP |
+                              LINK_STATUS_PHYSICAL_LINK_FLAG |
                               LINK_STATUS_AUTO_NEGOTIATE_COMPLETE |
                               LINK_STATUS_RX_FLOW_CONTROL_FLAG_MASK |
                               LINK_STATUS_TX_FLOW_CONTROL_FLAG_MASK |
@@ -6197,7 +6227,8 @@ static int bnx2x_update_link_up(struct link_params *params,
        u8 port = params->port;
        int rc = 0;
 
-       vars->link_status |= LINK_STATUS_LINK_UP;
+       vars->link_status |= (LINK_STATUS_LINK_UP |
+                             LINK_STATUS_PHYSICAL_LINK_FLAG);
        vars->phy_flags |= PHY_PHYSICAL_LINK_FLAG;
 
        if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX)
@@ -7998,6 +8029,9 @@ static void bnx2x_warpcore_set_limiting_mode(struct link_params *params,
        bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
                        MDIO_WC_REG_UC_INFO_B1_FIRMWARE_MODE, &val);
 
+       /* Restart microcode to re-read the new mode */
+       bnx2x_warpcore_reset_lane(bp, phy, 1);
+       bnx2x_warpcore_reset_lane(bp, phy, 0);
 
 }
 
@@ -8116,7 +8150,6 @@ void bnx2x_handle_module_detect_int(struct link_params *params)
                                 offsetof(struct shmem_region, dev_info.
                                          port_feature_config[params->port].
                                          config));
-
                bnx2x_set_gpio_int(bp, gpio_num,
                                   MISC_REGISTERS_GPIO_INT_OUTPUT_SET,
                                   gpio_port);
@@ -8125,8 +8158,9 @@ void bnx2x_handle_module_detect_int(struct link_params *params)
                 * Disable transmit for this module
                 */
                phy->media_type = ETH_PHY_NOT_PRESENT;
-               if ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) ==
-                   PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER)
+               if (((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) ==
+                    PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER) ||
+                   CHIP_IS_E3(bp))
                        bnx2x_sfp_set_transmitter(params, phy, 0);
        }
 }
@@ -8228,9 +8262,6 @@ static u8 bnx2x_8706_config_init(struct bnx2x_phy *phy,
        u16 cnt, val, tmp1;
        struct bnx2x *bp = params->bp;
 
-       /* SPF+ PHY: Set flag to check for Tx error */
-       vars->phy_flags = PHY_TX_ERROR_CHECK_FLAG;
-
        bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2,
                       MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port);
        /* HW reset */
@@ -8414,9 +8445,6 @@ static int bnx2x_8726_config_init(struct bnx2x_phy *phy,
        struct bnx2x *bp = params->bp;
        DP(NETIF_MSG_LINK, "Initializing BCM8726\n");
 
-       /* SPF+ PHY: Set flag to check for Tx error */
-       vars->phy_flags = PHY_TX_ERROR_CHECK_FLAG;
-
        bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 1<<15);
        bnx2x_wait_reset_complete(bp, phy, params);
 
@@ -8585,9 +8613,6 @@ static int bnx2x_8727_config_init(struct bnx2x_phy *phy,
        struct bnx2x *bp = params->bp;
        /* Enable PMD link, MOD_ABS_FLT, and 1G link alarm */
 
-       /* SPF+ PHY: Set flag to check for Tx error */
-       vars->phy_flags = PHY_TX_ERROR_CHECK_FLAG;
-
        bnx2x_wait_reset_complete(bp, phy, params);
        rx_alarm_ctrl_val = (1<<2) | (1<<5) ;
        /* Should be 0x6 to enable XS on Tx side. */
@@ -9243,7 +9268,13 @@ static int bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy,
        if (phy->req_duplex == DUPLEX_FULL)
                autoneg_val |= (1<<8);
 
-       bnx2x_cl45_write(bp, phy,
+       /*
+        * Always write this if this is not 84833.
+        * For 84833, write it only when it's a forced speed.
+        */
+       if ((phy->type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) ||
+               ((autoneg_val & (1<<12)) == 0))
+               bnx2x_cl45_write(bp, phy,
                         MDIO_AN_DEVAD,
                         MDIO_AN_REG_8481_LEGACY_MII_CTRL, autoneg_val);
 
@@ -9257,13 +9288,12 @@ static int bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy,
                        bnx2x_cl45_write(bp, phy,
                                 MDIO_AN_DEVAD, MDIO_AN_REG_CTRL,
                                 0x3200);
-       } else if (phy->req_line_speed != SPEED_10 &&
-                  phy->req_line_speed != SPEED_100) {
+       } else
                bnx2x_cl45_write(bp, phy,
                                 MDIO_AN_DEVAD,
                                 MDIO_AN_REG_8481_10GBASE_T_AN_CTRL,
                                 1);
-       }
+
        /* Save spirom version */
        bnx2x_save_848xx_spirom_version(phy, params);
 
@@ -9756,11 +9786,9 @@ static void bnx2x_848x3_link_reset(struct bnx2x_phy *phy,
                bnx2x_cl45_read(bp, phy,
                                MDIO_CTL_DEVAD,
                                0x400f, &val16);
-               /* Put to low power mode on newer FW */
-               if ((val16 & 0x303f) > 0x1009)
-                       bnx2x_cl45_write(bp, phy,
-                                       MDIO_PMA_DEVAD,
-                                       MDIO_PMA_REG_CTRL, 0x800);
+               bnx2x_cl45_write(bp, phy,
+                               MDIO_PMA_DEVAD,
+                               MDIO_PMA_REG_CTRL, 0x800);
        }
 }
 
@@ -10191,8 +10219,15 @@ static void bnx2x_54618se_link_reset(struct bnx2x_phy *phy,
        u32 cfg_pin;
        u8 port;
 
-       /* This works with E3 only, no need to check the chip
-          before determining the port. */
+       /*
+        * In case of no EPIO routed to reset the GPHY, put it
+        * in low power mode.
+        */
+       bnx2x_cl22_write(bp, phy, MDIO_PMA_REG_CTRL, 0x800);
+       /*
+        * This works with E3 only, no need to check the chip
+        * before determining the port.
+        */
        port = params->port;
        cfg_pin = (REG_RD(bp, params->shmem_base +
                        offsetof(struct shmem_region,
@@ -10603,7 +10638,8 @@ static struct bnx2x_phy phy_warpcore = {
        .type           = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT,
        .addr           = 0xff,
        .def_md_devad   = 0,
-       .flags          = FLAGS_HW_LOCK_REQUIRED,
+       .flags          = (FLAGS_HW_LOCK_REQUIRED |
+                          FLAGS_TX_ERROR_CHECK),
        .rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
        .tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
        .mdio_ctrl      = 0,
@@ -10729,7 +10765,8 @@ static struct bnx2x_phy phy_8706 = {
        .type           = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706,
        .addr           = 0xff,
        .def_md_devad   = 0,
-       .flags          = FLAGS_INIT_XGXS_FIRST,
+       .flags          = (FLAGS_INIT_XGXS_FIRST |
+                          FLAGS_TX_ERROR_CHECK),
        .rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
        .tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
        .mdio_ctrl      = 0,
@@ -10760,7 +10797,8 @@ static struct bnx2x_phy phy_8726 = {
        .addr           = 0xff,
        .def_md_devad   = 0,
        .flags          = (FLAGS_HW_LOCK_REQUIRED |
-                          FLAGS_INIT_XGXS_FIRST),
+                          FLAGS_INIT_XGXS_FIRST |
+                          FLAGS_TX_ERROR_CHECK),
        .rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
        .tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
        .mdio_ctrl      = 0,
@@ -10791,7 +10829,8 @@ static struct bnx2x_phy phy_8727 = {
        .type           = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727,
        .addr           = 0xff,
        .def_md_devad   = 0,
-       .flags          = FLAGS_FAN_FAILURE_DET_REQ,
+       .flags          = (FLAGS_FAN_FAILURE_DET_REQ |
+                          FLAGS_TX_ERROR_CHECK),
        .rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
        .tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff},
        .mdio_ctrl      = 0,
@@ -11112,6 +11151,8 @@ static int bnx2x_populate_int_phy(struct bnx2x *bp, u32 shmem_base, u8 port,
                 */
                if (CHIP_REV(bp) == CHIP_REV_Ax)
                        phy->flags |= FLAGS_MDC_MDIO_WA;
+               else
+                       phy->flags |= FLAGS_MDC_MDIO_WA_B0;
        } else {
                switch (switch_cfg) {
                case SWITCH_CFG_1G:
@@ -11500,13 +11541,12 @@ void bnx2x_init_xmac_loopback(struct link_params *params,
         * Set WC to loopback mode since link is required to provide clock
         * to the XMAC in 20G mode
         */
-       if (vars->line_speed == SPEED_20000) {
-               bnx2x_set_aer_mmd(params, &params->phy[0]);
-               bnx2x_warpcore_reset_lane(bp, &params->phy[0], 0);
-               params->phy[INT_PHY].config_loopback(
+       bnx2x_set_aer_mmd(params, &params->phy[0]);
+       bnx2x_warpcore_reset_lane(bp, &params->phy[0], 0);
+       params->phy[INT_PHY].config_loopback(
                        &params->phy[INT_PHY],
                        params);
-       }
+
        bnx2x_xmac_enable(params, vars, 1);
        REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0);
 }
@@ -11684,12 +11724,16 @@ int bnx2x_link_reset(struct link_params *params, struct link_vars *vars,
        bnx2x_set_led(params, vars, LED_MODE_OFF, 0);
 
        if (reset_ext_phy) {
+               bnx2x_set_mdio_clk(bp, params->chip_id, port);
                for (phy_index = EXT_PHY1; phy_index < params->num_phys;
                      phy_index++) {
-                       if (params->phy[phy_index].link_reset)
+                       if (params->phy[phy_index].link_reset) {
+                               bnx2x_set_aer_mmd(params,
+                                                 &params->phy[phy_index]);
                                params->phy[phy_index].link_reset(
                                        &params->phy[phy_index],
                                        params);
+                       }
                        if (params->phy[phy_index].flags &
                            FLAGS_REARM_LATCH_SIGNAL)
                                clear_latch_ind = 1;
@@ -12178,10 +12222,6 @@ static void bnx2x_analyze_link_error(struct link_params *params,
        u8 led_mode;
        u32 half_open_conn = (vars->phy_flags & PHY_HALF_OPEN_CONN_FLAG) > 0;
 
-       /*DP(NETIF_MSG_LINK, "CHECK LINK: %x half_open:%x-> lss:%x\n",
-                      vars->link_up,
-                      half_open_conn, lss_status);*/
-
        if ((lss_status ^ half_open_conn) == 0)
                return;
 
@@ -12194,6 +12234,7 @@ static void bnx2x_analyze_link_error(struct link_params *params,
         * b. Update link_vars->link_up
         */
        if (lss_status) {
+               DP(NETIF_MSG_LINK, "Remote Fault detected !!!\n");
                vars->link_status &= ~LINK_STATUS_LINK_UP;
                vars->link_up = 0;
                vars->phy_flags |= PHY_HALF_OPEN_CONN_FLAG;
@@ -12203,6 +12244,7 @@ static void bnx2x_analyze_link_error(struct link_params *params,
                 */
                led_mode = LED_MODE_OFF;
        } else {
+               DP(NETIF_MSG_LINK, "Remote Fault cleared\n");
                vars->link_status |= LINK_STATUS_LINK_UP;
                vars->link_up = 1;
                vars->phy_flags &= ~PHY_HALF_OPEN_CONN_FLAG;
@@ -12219,6 +12261,15 @@ static void bnx2x_analyze_link_error(struct link_params *params,
        bnx2x_notify_link_changed(bp);
 }
 
+/******************************************************************************
+* Description:
+*      This function checks for half opened connection change indication.
+*      When such change occurs, it calls the bnx2x_analyze_link_error
+*      to check if Remote Fault is set or cleared. Reception of remote fault
+*      status message in the MAC indicates that the peer's MAC has detected
+*      a fault, for example, due to break in the TX side of fiber.
+*
+******************************************************************************/
 static void bnx2x_check_half_open_conn(struct link_params *params,
                                       struct link_vars *vars)
 {
@@ -12229,9 +12280,28 @@ static void bnx2x_check_half_open_conn(struct link_params *params,
        if ((vars->phy_flags & PHY_PHYSICAL_LINK_FLAG) == 0)
                return;
 
-       if (!CHIP_IS_E3(bp) &&
+       if (CHIP_IS_E3(bp) &&
            (REG_RD(bp, MISC_REG_RESET_REG_2) &
-                  (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << params->port))) {
+             (MISC_REGISTERS_RESET_REG_2_XMAC))) {
+               /* Check E3 XMAC */
+               /*
+                * Note that link speed cannot be queried here, since it may be
+                * zero while link is down. In case UMAC is active, LSS will
+                * simply not be set
+                */
+               mac_base = (params->port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
+
+               /* Clear stick bits (Requires rising edge) */
+               REG_WR(bp, mac_base + XMAC_REG_CLEAR_RX_LSS_STATUS, 0);
+               REG_WR(bp, mac_base + XMAC_REG_CLEAR_RX_LSS_STATUS,
+                      XMAC_CLEAR_RX_LSS_STATUS_REG_CLEAR_LOCAL_FAULT_STATUS |
+                      XMAC_CLEAR_RX_LSS_STATUS_REG_CLEAR_REMOTE_FAULT_STATUS);
+               if (REG_RD(bp, mac_base + XMAC_REG_RX_LSS_STATUS))
+                       lss_status = 1;
+
+               bnx2x_analyze_link_error(params, vars, lss_status);
+       } else if (REG_RD(bp, MISC_REG_RESET_REG_2) &
+                  (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << params->port)) {
                /* Check E1X / E2 BMAC */
                u32 lss_status_reg;
                u32 wb_data[2];
@@ -12253,14 +12323,20 @@ static void bnx2x_check_half_open_conn(struct link_params *params,
 void bnx2x_period_func(struct link_params *params, struct link_vars *vars)
 {
        struct bnx2x *bp = params->bp;
+       u16 phy_idx;
        if (!params) {
-               DP(NETIF_MSG_LINK, "Ininitliazed params !\n");
+               DP(NETIF_MSG_LINK, "Uninitialized params !\n");
                return;
        }
-       /* DP(NETIF_MSG_LINK, "Periodic called vars->phy_flags 0x%x speed 0x%x
-        RESET_REG_2 0x%x\n", vars->phy_flags, vars->line_speed,
-         REG_RD(bp, MISC_REG_RESET_REG_2)); */
-       bnx2x_check_half_open_conn(params, vars);
+
+       for (phy_idx = INT_PHY; phy_idx < MAX_PHYS; phy_idx++) {
+               if (params->phy[phy_idx].flags & FLAGS_TX_ERROR_CHECK) {
+                       bnx2x_set_aer_mmd(params, &params->phy[phy_idx]);
+                       bnx2x_check_half_open_conn(params, vars);
+                       break;
+               }
+       }
+
        if (CHIP_IS_E3(bp))
                bnx2x_check_over_curr(params, vars);
 }
index 6a7708d5da37fe02b11c4c675ae885ca5d4e96fc..c12db6da213efe7f25da6000774de35b78939642 100644 (file)
@@ -145,6 +145,8 @@ struct bnx2x_phy {
 #define FLAGS_SFP_NOT_APPROVED         (1<<7)
 #define FLAGS_MDC_MDIO_WA              (1<<8)
 #define FLAGS_DUMMY_READ               (1<<9)
+#define FLAGS_MDC_MDIO_WA_B0           (1<<10)
+#define FLAGS_TX_ERROR_CHECK           (1<<12)
 
        /* preemphasis values for the rx side */
        u16 rx_preemphasis[4];
@@ -276,7 +278,6 @@ struct link_vars {
 #define PHY_PHYSICAL_LINK_FLAG         (1<<2)
 #define PHY_HALF_OPEN_CONN_FLAG                (1<<3)
 #define PHY_OVER_CURRENT_FLAG          (1<<4)
-#define PHY_TX_ERROR_CHECK_FLAG                (1<<5)
 
        u8 mac_type;
 #define MAC_TYPE_NONE          0
index 02461fef8751821c9f8a7c9501ebc79d363817b8..27b5ecb11830c383f7b17e1545aa52b9c19600cc 100644 (file)
    The fields are: [4:0] - tail pointer; 10:5] - Link List size; 15:11] -
    header pointer. */
 #define UCM_REG_XX_TABLE                                        0xe0300
+#define UMAC_COMMAND_CONFIG_REG_IGNORE_TX_PAUSE                         (0x1<<28)
 #define UMAC_COMMAND_CONFIG_REG_LOOP_ENA                        (0x1<<15)
 #define UMAC_COMMAND_CONFIG_REG_NO_LGTH_CHECK                   (0x1<<24)
 #define UMAC_COMMAND_CONFIG_REG_PAD_EN                          (0x1<<5)
+#define UMAC_COMMAND_CONFIG_REG_PAUSE_IGNORE                    (0x1<<8)
 #define UMAC_COMMAND_CONFIG_REG_PROMIS_EN                       (0x1<<4)
 #define UMAC_COMMAND_CONFIG_REG_RX_ENA                          (0x1<<1)
 #define UMAC_COMMAND_CONFIG_REG_SW_RESET                        (0x1<<13)
 #define EMAC_MDIO_COMM_START_BUSY                               (1L<<29)
 #define EMAC_MDIO_MODE_AUTO_POLL                                (1L<<4)
 #define EMAC_MDIO_MODE_CLAUSE_45                                (1L<<31)
-#define EMAC_MDIO_MODE_CLOCK_CNT                                (0x3fL<<16)
+#define EMAC_MDIO_MODE_CLOCK_CNT                                (0x3ffL<<16)
 #define EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT                       16
+#define EMAC_MDIO_STATUS_10MB                                   (1L<<1)
 #define EMAC_MODE_25G_MODE                                      (1L<<5)
 #define EMAC_MODE_HALF_DUPLEX                                   (1L<<1)
 #define EMAC_MODE_PORT_GMII                                     (2L<<2)
 #define EMAC_REG_EMAC_MAC_MATCH                                 0x10
 #define EMAC_REG_EMAC_MDIO_COMM                                 0xac
 #define EMAC_REG_EMAC_MDIO_MODE                                 0xb4
+#define EMAC_REG_EMAC_MDIO_STATUS                               0xb0
 #define EMAC_REG_EMAC_MODE                                      0x0
 #define EMAC_REG_EMAC_RX_MODE                                   0xc8
 #define EMAC_REG_EMAC_RX_MTU_SIZE                               0x9c
index c5f0f04219f3a3040d04d6a34f8e311b1ad22d29..5548d464261a6173fc1be5588960d858b33e8592 100644 (file)
@@ -838,6 +838,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
 
        /* Disable all the interrupts */
        ew32(IMC, 0xFFFFFFFF);
+       E1000_WRITE_FLUSH();
        msleep(10);
 
        /* Test each interrupt */
@@ -856,6 +857,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
                        adapter->test_icr = 0;
                        ew32(IMC, mask);
                        ew32(ICS, mask);
+                       E1000_WRITE_FLUSH();
                        msleep(10);
 
                        if (adapter->test_icr & mask) {
@@ -873,6 +875,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
                adapter->test_icr = 0;
                ew32(IMS, mask);
                ew32(ICS, mask);
+               E1000_WRITE_FLUSH();
                msleep(10);
 
                if (!(adapter->test_icr & mask)) {
@@ -890,6 +893,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
                        adapter->test_icr = 0;
                        ew32(IMC, ~mask & 0x00007FFF);
                        ew32(ICS, ~mask & 0x00007FFF);
+                       E1000_WRITE_FLUSH();
                        msleep(10);
 
                        if (adapter->test_icr) {
@@ -901,6 +905,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
 
        /* Disable all the interrupts */
        ew32(IMC, 0xFFFFFFFF);
+       E1000_WRITE_FLUSH();
        msleep(10);
 
        /* Unhook test interrupt handler */
@@ -1394,6 +1399,7 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter)
                        if (unlikely(++k == txdr->count)) k = 0;
                }
                ew32(TDT, k);
+               E1000_WRITE_FLUSH();
                msleep(200);
                time = jiffies; /* set the start time for the receive */
                good_cnt = 0;
index 1698622af434495d08fb07888e26bad4d6d41064..8545c7aa93eb674639b6d68dff2e77cb9a7e7d75 100644 (file)
@@ -446,6 +446,7 @@ s32 e1000_reset_hw(struct e1000_hw *hw)
        /* Must reset the PHY before resetting the MAC */
        if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) {
                ew32(CTRL, (ctrl | E1000_CTRL_PHY_RST));
+               E1000_WRITE_FLUSH();
                msleep(5);
        }
 
@@ -3752,6 +3753,7 @@ static s32 e1000_acquire_eeprom(struct e1000_hw *hw)
                /* Clear SK and CS */
                eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
                ew32(EECD, eecd);
+               E1000_WRITE_FLUSH();
                udelay(1);
        }
 
@@ -3824,6 +3826,7 @@ static void e1000_release_eeprom(struct e1000_hw *hw)
                eecd &= ~E1000_EECD_SK; /* Lower SCK */
 
                ew32(EECD, eecd);
+               E1000_WRITE_FLUSH();
 
                udelay(hw->eeprom.delay_usec);
        } else if (hw->eeprom.type == e1000_eeprom_microwire) {
index c0ecb2d9fdb7abc1a47a240af1c5698f4e1b3edc..e4f42257c24c9743c5d6b135694930284f7b1f62 100644 (file)
@@ -1313,6 +1313,7 @@ static s32 e1000_read_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
        kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
                       E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
        ew32(KMRNCTRLSTA, kmrnctrlsta);
+       e1e_flush();
 
        udelay(2);
 
@@ -1347,6 +1348,7 @@ static s32 e1000_write_kmrn_reg_80003es2lan(struct e1000_hw *hw, u32 offset,
        kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
                       E1000_KMRNCTRLSTA_OFFSET) | data;
        ew32(KMRNCTRLSTA, kmrnctrlsta);
+       e1e_flush();
 
        udelay(2);
 
index cb1a3623253e8704f6d724a53b8ab8ad896eaa6a..06d88f316dce4de827975cc5abdfbf4d2700eb41 100644 (file)
@@ -28,8 +28,8 @@
 
 /* ethtool support for e1000 */
 
-#include <linux/interrupt.h>
 #include <linux/netdevice.h>
+#include <linux/interrupt.h>
 #include <linux/ethtool.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
@@ -964,6 +964,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
 
        /* Disable all the interrupts */
        ew32(IMC, 0xFFFFFFFF);
+       e1e_flush();
        usleep_range(10000, 20000);
 
        /* Test each interrupt */
@@ -996,6 +997,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
                        adapter->test_icr = 0;
                        ew32(IMC, mask);
                        ew32(ICS, mask);
+                       e1e_flush();
                        usleep_range(10000, 20000);
 
                        if (adapter->test_icr & mask) {
@@ -1014,6 +1016,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
                adapter->test_icr = 0;
                ew32(IMS, mask);
                ew32(ICS, mask);
+               e1e_flush();
                usleep_range(10000, 20000);
 
                if (!(adapter->test_icr & mask)) {
@@ -1032,6 +1035,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
                        adapter->test_icr = 0;
                        ew32(IMC, ~mask & 0x00007FFF);
                        ew32(ICS, ~mask & 0x00007FFF);
+                       e1e_flush();
                        usleep_range(10000, 20000);
 
                        if (adapter->test_icr) {
@@ -1043,6 +1047,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data)
 
        /* Disable all the interrupts */
        ew32(IMC, 0xFFFFFFFF);
+       e1e_flush();
        usleep_range(10000, 20000);
 
        /* Unhook test interrupt handler */
@@ -1276,6 +1281,7 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter)
                             E1000_CTRL_FD);     /* Force Duplex to FULL */
 
                ew32(CTRL, ctrl_reg);
+               e1e_flush();
                udelay(500);
 
                return 0;
@@ -1418,6 +1424,7 @@ static int e1000_set_82571_fiber_loopback(struct e1000_adapter *adapter)
         */
 #define E1000_SERDES_LB_ON 0x410
        ew32(SCTL, E1000_SERDES_LB_ON);
+       e1e_flush();
        usleep_range(10000, 20000);
 
        return 0;
@@ -1513,6 +1520,7 @@ static void e1000_loopback_cleanup(struct e1000_adapter *adapter)
                    hw->phy.media_type == e1000_media_type_internal_serdes) {
 #define E1000_SERDES_LB_OFF 0x400
                        ew32(SCTL, E1000_SERDES_LB_OFF);
+                       e1e_flush();
                        usleep_range(10000, 20000);
                        break;
                }
@@ -1592,6 +1600,7 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter)
                                k = 0;
                }
                ew32(TDT, k);
+               e1e_flush();
                msleep(200);
                time = jiffies; /* set the start time for the receive */
                good_cnt = 0;
index c1752124f3cdf2572e89b48db537a26783c4b33d..4e36978b8fd8f11c2042d611165a28b119111ad1 100644 (file)
@@ -283,6 +283,7 @@ static void e1000_toggle_lanphypc_value_ich8lan(struct e1000_hw *hw)
        ctrl |= E1000_CTRL_LANPHYPC_OVERRIDE;
        ctrl &= ~E1000_CTRL_LANPHYPC_VALUE;
        ew32(CTRL, ctrl);
+       e1e_flush();
        udelay(10);
        ctrl &= ~E1000_CTRL_LANPHYPC_OVERRIDE;
        ew32(CTRL, ctrl);
@@ -1230,9 +1231,11 @@ s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable)
        ew32(CTRL, reg);
 
        ew32(CTRL_EXT, ctrl_ext | E1000_CTRL_EXT_SPD_BYPS);
+       e1e_flush();
        udelay(20);
        ew32(CTRL, ctrl_reg);
        ew32(CTRL_EXT, ctrl_ext);
+       e1e_flush();
        udelay(20);
 
 out:
@@ -2134,8 +2137,7 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words,
 
        ret_val = 0;
        for (i = 0; i < words; i++) {
-               if ((dev_spec->shadow_ram) &&
-                   (dev_spec->shadow_ram[offset+i].modified)) {
+               if (dev_spec->shadow_ram[offset+i].modified) {
                        data[i] = dev_spec->shadow_ram[offset+i].value;
                } else {
                        ret_val = e1000_read_flash_word_ich8lan(hw,
@@ -3090,6 +3092,7 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw)
        ret_val = e1000_acquire_swflag_ich8lan(hw);
        e_dbg("Issuing a global reset to ich8lan\n");
        ew32(CTRL, (ctrl | E1000_CTRL_RST));
+       /* cannot issue a flush here because it hangs the hardware */
        msleep(20);
 
        if (!ret_val)
index 65580b40594207a2aac7a663da5bc0c105a67cb2..7898a67d6505478036e19e790ebeb1a1f2295445 100644 (file)
@@ -1986,6 +1986,7 @@ static s32 e1000_ready_nvm_eeprom(struct e1000_hw *hw)
                /* Clear SK and CS */
                eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
                ew32(EECD, eecd);
+               e1e_flush();
                udelay(1);
 
                /*
index 4353ad56cf166d1e4b2939a303b6358004326d00..ab4be80f7ab53bd01a28dd911a0dbabd26c27235 100644 (file)
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/init.h>
-#include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/vmalloc.h>
 #include <linux/pagemap.h>
 #include <linux/delay.h>
 #include <linux/netdevice.h>
+#include <linux/interrupt.h>
 #include <linux/tcp.h>
 #include <linux/ipv6.h>
 #include <linux/slab.h>
index 2a6ee13285b19bf49e53485d03da830e1ef0cbe7..8666476cb9be4d723a3dc07f5feb66230aa86fb5 100644 (file)
@@ -537,6 +537,7 @@ static s32 __e1000_read_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 *data,
        kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
                       E1000_KMRNCTRLSTA_OFFSET) | E1000_KMRNCTRLSTA_REN;
        ew32(KMRNCTRLSTA, kmrnctrlsta);
+       e1e_flush();
 
        udelay(2);
 
@@ -609,6 +610,7 @@ static s32 __e1000_write_kmrn_reg(struct e1000_hw *hw, u32 offset, u16 data,
        kmrnctrlsta = ((offset << E1000_KMRNCTRLSTA_OFFSET_SHIFT) &
                       E1000_KMRNCTRLSTA_OFFSET) | data;
        ew32(KMRNCTRLSTA, kmrnctrlsta);
+       e1e_flush();
 
        udelay(2);
 
index 7dcd65cede562d4cf47d83d4f4c2d742cb5ae241..40407124e7222090c364aa960cfc303115a9571f 100644 (file)
@@ -285,6 +285,7 @@ static s32 igb_ready_nvm_eeprom(struct e1000_hw *hw)
                /* Clear SK and CS */
                eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
                wr32(E1000_EECD, eecd);
+               wrfl();
                udelay(1);
                timeout = NVM_MAX_RETRY_SPI;
 
index ff244ce803ceb51d95d033bcdbdbe85b47d4a499..414b0225be897fcaa51f19d9f417dd4a5140f956 100644 (file)
@@ -1225,6 +1225,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
 
        /* Disable all the interrupts */
        wr32(E1000_IMC, ~0);
+       wrfl();
        msleep(10);
 
        /* Define all writable bits for ICS */
@@ -1268,6 +1269,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
 
                        wr32(E1000_IMC, mask);
                        wr32(E1000_ICS, mask);
+                       wrfl();
                        msleep(10);
 
                        if (adapter->test_icr & mask) {
@@ -1289,6 +1291,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
 
                wr32(E1000_IMS, mask);
                wr32(E1000_ICS, mask);
+               wrfl();
                msleep(10);
 
                if (!(adapter->test_icr & mask)) {
@@ -1310,6 +1313,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
 
                        wr32(E1000_IMC, ~mask);
                        wr32(E1000_ICS, ~mask);
+                       wrfl();
                        msleep(10);
 
                        if (adapter->test_icr & mask) {
@@ -1321,6 +1325,7 @@ static int igb_intr_test(struct igb_adapter *adapter, u64 *data)
 
        /* Disable all the interrupts */
        wr32(E1000_IMC, ~0);
+       wrfl();
        msleep(10);
 
        /* Unhook test interrupt handler */
index dc599059512aa6ff9fb249225108130f988898e0..40d4c405fd7e0218703c091619a632b8525cd2c6 100644 (file)
@@ -1052,6 +1052,7 @@ msi_only:
                kfree(adapter->vf_data);
                adapter->vf_data = NULL;
                wr32(E1000_IOVCTL, E1000_IOVCTL_REUSE_VFQ);
+               wrfl();
                msleep(100);
                dev_info(&adapter->pdev->dev, "IOV Disabled\n");
        }
@@ -2022,7 +2023,7 @@ static int __devinit igb_probe(struct pci_dev *pdev,
 
        if (hw->bus.func == 0)
                hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data);
-       else if (hw->mac.type == e1000_82580)
+       else if (hw->mac.type >= e1000_82580)
                hw->nvm.ops.read(hw, NVM_INIT_CONTROL3_PORT_A +
                                 NVM_82580_LAN_FUNC_OFFSET(hw->bus.func), 1,
                                 &eeprom_data);
@@ -2198,6 +2199,7 @@ static void __devexit igb_remove(struct pci_dev *pdev)
                kfree(adapter->vf_data);
                adapter->vf_data = NULL;
                wr32(E1000_IOVCTL, E1000_IOVCTL_REUSE_VFQ);
+               wrfl();
                msleep(100);
                dev_info(&pdev->dev, "IOV Disabled\n");
        }
index 1330c8e932da3683fa1e3680e1979d0ae146ba11..40ed066e3ef48177cca4725405c53b78cd6af786 100644 (file)
@@ -1226,6 +1226,7 @@ static void igbvf_configure_tx(struct igbvf_adapter *adapter)
        /* disable transmits */
        txdctl = er32(TXDCTL(0));
        ew32(TXDCTL(0), txdctl & ~E1000_TXDCTL_QUEUE_ENABLE);
+       e1e_flush();
        msleep(10);
 
        /* Setup the HW Tx Head and Tail descriptor pointers */
@@ -1306,6 +1307,7 @@ static void igbvf_configure_rx(struct igbvf_adapter *adapter)
        /* disable receives */
        rxdctl = er32(RXDCTL(0));
        ew32(RXDCTL(0), rxdctl & ~E1000_RXDCTL_QUEUE_ENABLE);
+       e1e_flush();
        msleep(10);
 
        rdlen = rx_ring->count * sizeof(union e1000_adv_rx_desc);
index 954f6e938fb7d926c5430906e6bdffd5d40ba6f2..8b1c3484d271ac6a7aa6640d268e9e7013b52535 100644 (file)
@@ -2405,8 +2405,6 @@ static int __init smsc_superio_lpc(unsigned short cfg_base)
  * addresses making a subsystem device table necessary.
  */
 #ifdef CONFIG_PCI
-#define PCIID_VENDOR_INTEL 0x8086
-#define PCIID_VENDOR_ALI 0x10b9
 static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __initdata = {
        /*
         * Subsystems needing entries:
@@ -2416,7 +2414,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
         */
        {
                /* Guessed entry */
-               .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+               .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
                .device = 0x24cc,
                .subvendor = 0x103c,
                .subdevice = 0x08bc,
@@ -2429,7 +2427,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
                .name = "HP nx5000 family",
        },
        {
-               .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+               .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
                .device = 0x24cc,
                .subvendor = 0x103c,
                .subdevice = 0x088c,
@@ -2443,7 +2441,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
                .name = "HP nc8000 family",
        },
        {
-               .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+               .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
                .device = 0x24cc,
                .subvendor = 0x103c,
                .subdevice = 0x0890,
@@ -2456,7 +2454,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
                .name = "HP nc6000 family",
        },
        {
-               .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+               .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801DBM LPC bridge */
                .device = 0x24cc,
                .subvendor = 0x0e11,
                .subdevice = 0x0860,
@@ -2471,7 +2469,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
        },
        {
                /* Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge */
-               .vendor = PCIID_VENDOR_INTEL,
+               .vendor = PCI_VENDOR_ID_INTEL,
                .device = 0x24c0,
                .subvendor = 0x1179,
                .subdevice = 0xffff, /* 0xffff is "any" */
@@ -2484,7 +2482,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
                .name = "Toshiba laptop with Intel 82801DB/DBL LPC bridge",
        },
        {
-               .vendor = PCIID_VENDOR_INTEL, /* Intel 82801CAM ISA bridge */
+               .vendor = PCI_VENDOR_ID_INTEL, /* Intel 82801CAM ISA bridge */
                .device = 0x248c,
                .subvendor = 0x1179,
                .subdevice = 0xffff, /* 0xffff is "any" */
@@ -2498,7 +2496,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
        },
        {
                /* 82801DBM (ICH4-M) LPC Interface Bridge */
-               .vendor = PCIID_VENDOR_INTEL,
+               .vendor = PCI_VENDOR_ID_INTEL,
                .device = 0x24cc,
                .subvendor = 0x1179,
                .subdevice = 0xffff, /* 0xffff is "any" */
@@ -2512,7 +2510,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
        },
        {
                /* ALi M1533/M1535 PCI to ISA Bridge [Aladdin IV/V/V+] */
-               .vendor = PCIID_VENDOR_ALI,
+               .vendor = PCI_VENDOR_ID_AL,
                .device = 0x1533,
                .subvendor = 0x1179,
                .subdevice = 0xffff, /* 0xffff is "any" */
index c982ab9f9005179b1910a75f45404442dfb9fec5..38b362b678572e45f8b316b7cc47e978f9091189 100644 (file)
@@ -57,6 +57,7 @@ ixgb_raise_clock(struct ixgb_hw *hw,
         */
        *eecd_reg = *eecd_reg | IXGB_EECD_SK;
        IXGB_WRITE_REG(hw, EECD, *eecd_reg);
+       IXGB_WRITE_FLUSH(hw);
        udelay(50);
 }
 
@@ -75,6 +76,7 @@ ixgb_lower_clock(struct ixgb_hw *hw,
         */
        *eecd_reg = *eecd_reg & ~IXGB_EECD_SK;
        IXGB_WRITE_REG(hw, EECD, *eecd_reg);
+       IXGB_WRITE_FLUSH(hw);
        udelay(50);
 }
 
@@ -112,6 +114,7 @@ ixgb_shift_out_bits(struct ixgb_hw *hw,
                        eecd_reg |= IXGB_EECD_DI;
 
                IXGB_WRITE_REG(hw, EECD, eecd_reg);
+               IXGB_WRITE_FLUSH(hw);
 
                udelay(50);
 
@@ -206,21 +209,25 @@ ixgb_standby_eeprom(struct ixgb_hw *hw)
        /*  Deselect EEPROM  */
        eecd_reg &= ~(IXGB_EECD_CS | IXGB_EECD_SK);
        IXGB_WRITE_REG(hw, EECD, eecd_reg);
+       IXGB_WRITE_FLUSH(hw);
        udelay(50);
 
        /*  Clock high  */
        eecd_reg |= IXGB_EECD_SK;
        IXGB_WRITE_REG(hw, EECD, eecd_reg);
+       IXGB_WRITE_FLUSH(hw);
        udelay(50);
 
        /*  Select EEPROM  */
        eecd_reg |= IXGB_EECD_CS;
        IXGB_WRITE_REG(hw, EECD, eecd_reg);
+       IXGB_WRITE_FLUSH(hw);
        udelay(50);
 
        /*  Clock low  */
        eecd_reg &= ~IXGB_EECD_SK;
        IXGB_WRITE_REG(hw, EECD, eecd_reg);
+       IXGB_WRITE_FLUSH(hw);
        udelay(50);
 }
 
@@ -239,11 +246,13 @@ ixgb_clock_eeprom(struct ixgb_hw *hw)
        /*  Rising edge of clock  */
        eecd_reg |= IXGB_EECD_SK;
        IXGB_WRITE_REG(hw, EECD, eecd_reg);
+       IXGB_WRITE_FLUSH(hw);
        udelay(50);
 
        /*  Falling edge of clock  */
        eecd_reg &= ~IXGB_EECD_SK;
        IXGB_WRITE_REG(hw, EECD, eecd_reg);
+       IXGB_WRITE_FLUSH(hw);
        udelay(50);
 }
 
index 6cb2e42ff4c135d2bf171b2b5e2e6ec57da8f271..3d61a9e4faf761e619d61001bc3ce83f21211800 100644 (file)
@@ -149,6 +149,7 @@ ixgb_adapter_stop(struct ixgb_hw *hw)
         */
        IXGB_WRITE_REG(hw, RCTL, IXGB_READ_REG(hw, RCTL) & ~IXGB_RCTL_RXEN);
        IXGB_WRITE_REG(hw, TCTL, IXGB_READ_REG(hw, TCTL) & ~IXGB_TCTL_TXEN);
+       IXGB_WRITE_FLUSH(hw);
        msleep(IXGB_DELAY_BEFORE_RESET);
 
        /* Issue a global reset to the MAC.  This will reset the chip's
@@ -1220,6 +1221,7 @@ ixgb_optics_reset_bcm(struct ixgb_hw *hw)
        ctrl &= ~IXGB_CTRL0_SDP2;
        ctrl |= IXGB_CTRL0_SDP3;
        IXGB_WRITE_REG(hw, CTRL0, ctrl);
+       IXGB_WRITE_FLUSH(hw);
 
        /* SerDes needs extra delay */
        msleep(IXGB_SUN_PHY_RESET_DELAY);
index 3b3dd4df4c5ccad551ad89418d198f3781375ce7..34f30ec79c2ebea6af3d5475339d51238a8bc73d 100644 (file)
@@ -213,6 +213,7 @@ static s32 ixgbe_init_phy_ops_82599(struct ixgbe_hw *hw)
        switch (hw->phy.type) {
        case ixgbe_phy_tn:
                phy->ops.check_link = &ixgbe_check_phy_link_tnx;
+               phy->ops.setup_link = &ixgbe_setup_phy_link_tnx;
                phy->ops.get_firmware_version =
                             &ixgbe_get_phy_firmware_version_tnx;
                break;
index 777051f54e532eb5f97dea9c22d2a26b7c66b13f..fc1375f26fe56a5991f434ab70a1069fb01d01a8 100644 (file)
@@ -2632,6 +2632,7 @@ s32 ixgbe_blink_led_start_generic(struct ixgbe_hw *hw, u32 index)
                autoc_reg |= IXGBE_AUTOC_AN_RESTART;
                autoc_reg |= IXGBE_AUTOC_FLU;
                IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc_reg);
+               IXGBE_WRITE_FLUSH(hw);
                usleep_range(10000, 20000);
        }
 
index dc649553a0a65a837e4053e57c0ab42b66af6f32..82d4244c6e1050a416dfe2a939ca7c15d095a125 100644 (file)
@@ -1378,6 +1378,7 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
 
        /* Disable all the interrupts */
        IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, 0xFFFFFFFF);
+       IXGBE_WRITE_FLUSH(&adapter->hw);
        usleep_range(10000, 20000);
 
        /* Test each interrupt */
@@ -1398,6 +1399,7 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
                                        ~mask & 0x00007FFF);
                        IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS,
                                        ~mask & 0x00007FFF);
+                       IXGBE_WRITE_FLUSH(&adapter->hw);
                        usleep_range(10000, 20000);
 
                        if (adapter->test_icr & mask) {
@@ -1415,6 +1417,7 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
                adapter->test_icr = 0;
                IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, mask);
                IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS, mask);
+               IXGBE_WRITE_FLUSH(&adapter->hw);
                usleep_range(10000, 20000);
 
                if (!(adapter->test_icr &mask)) {
@@ -1435,6 +1438,7 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
                                        ~mask & 0x00007FFF);
                        IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS,
                                        ~mask & 0x00007FFF);
+                       IXGBE_WRITE_FLUSH(&adapter->hw);
                        usleep_range(10000, 20000);
 
                        if (adapter->test_icr) {
@@ -1446,6 +1450,7 @@ static int ixgbe_intr_test(struct ixgbe_adapter *adapter, u64 *data)
 
        /* Disable all the interrupts */
        IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, 0xFFFFFFFF);
+       IXGBE_WRITE_FLUSH(&adapter->hw);
        usleep_range(10000, 20000);
 
        /* Unhook test interrupt handler */
index 1be617545dc9bdbbfb9a39b0be78c9a9c967337c..e86297b32733e8a0af34a329a3f839ce2c57205b 100644 (file)
@@ -184,6 +184,7 @@ static inline void ixgbe_disable_sriov(struct ixgbe_adapter *adapter)
        vmdctl = IXGBE_READ_REG(hw, IXGBE_VT_CTL);
        vmdctl &= ~IXGBE_VT_CTL_POOL_MASK;
        IXGBE_WRITE_REG(hw, IXGBE_VT_CTL, vmdctl);
+       IXGBE_WRITE_FLUSH(hw);
 
        /* take a breather then clean up driver data */
        msleep(100);
@@ -1005,7 +1006,7 @@ static int __ixgbe_notify_dca(struct device *dev, void *data)
        struct ixgbe_adapter *adapter = dev_get_drvdata(dev);
        unsigned long event = *(unsigned long *)data;
 
-       if (!(adapter->flags & IXGBE_FLAG_DCA_ENABLED))
+       if (!(adapter->flags & IXGBE_FLAG_DCA_CAPABLE))
                return 0;
 
        switch (event) {
index 735f686c3b36605eff5abc5573a05ef87dbe17d5..f7ca3511b9fede0657bc69141b84b02d000c72c6 100644 (file)
@@ -1585,6 +1585,7 @@ static s32 ixgbe_raise_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl)
        *i2cctl |= IXGBE_I2C_CLK_OUT;
 
        IXGBE_WRITE_REG(hw, IXGBE_I2CCTL, *i2cctl);
+       IXGBE_WRITE_FLUSH(hw);
 
        /* SCL rise time (1000ns) */
        udelay(IXGBE_I2C_T_RISE);
@@ -1605,6 +1606,7 @@ static void ixgbe_lower_i2c_clk(struct ixgbe_hw *hw, u32 *i2cctl)
        *i2cctl &= ~IXGBE_I2C_CLK_OUT;
 
        IXGBE_WRITE_REG(hw, IXGBE_I2CCTL, *i2cctl);
+       IXGBE_WRITE_FLUSH(hw);
 
        /* SCL fall time (300ns) */
        udelay(IXGBE_I2C_T_FALL);
@@ -1628,6 +1630,7 @@ static s32 ixgbe_set_i2c_data(struct ixgbe_hw *hw, u32 *i2cctl, bool data)
                *i2cctl &= ~IXGBE_I2C_DATA_OUT;
 
        IXGBE_WRITE_REG(hw, IXGBE_I2CCTL, *i2cctl);
+       IXGBE_WRITE_FLUSH(hw);
 
        /* Data rise/fall (1000ns/300ns) and set-up time (250ns) */
        udelay(IXGBE_I2C_T_RISE + IXGBE_I2C_T_FALL + IXGBE_I2C_T_SU_DATA);
index bec30ed91adc9a1059305b9bcea4445bd919601f..2696c78e9f46c65a4971caadbfc0ace41cb65571 100644 (file)
@@ -162,6 +162,7 @@ mac_reset_top:
        ctrl_ext = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT);
        ctrl_ext |= IXGBE_CTRL_EXT_PFRSTD;
        IXGBE_WRITE_REG(hw, IXGBE_CTRL_EXT, ctrl_ext);
+       IXGBE_WRITE_FLUSH(hw);
 
        msleep(50);
 
index 0fcdc25699d8880238d9db011cd084a3ecc3b3de..dc4e305a1087c69a0e09b4cbe50b189ff15b8cc1 100644 (file)
@@ -322,6 +322,9 @@ static void macb_tx(struct macb *bp)
                for (i = 0; i < TX_RING_SIZE; i++)
                        bp->tx_ring[i].ctrl = MACB_BIT(TX_USED);
 
+               /* Add wrap bit */
+               bp->tx_ring[TX_RING_SIZE - 1].ctrl |= MACB_BIT(TX_WRAP);
+
                /* free transmit buffer in upper layer*/
                for (tail = bp->tx_tail; tail != head; tail = NEXT_TX(tail)) {
                        struct ring_info *rp = &bp->tx_skb[tail];
index 5e7109178061ec987594171fb0eb6ad6685c6906..5ada5b4691127fa54026619d20de17596dac9581 100644 (file)
@@ -128,7 +128,7 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn,
        memset(context, 0, sizeof *context);
 
        context->base_qpn = cpu_to_be32(base_qpn);
-       context->n_mac = 0x7;
+       context->n_mac = 0x2;
        context->promisc = cpu_to_be32(promisc << SET_PORT_PROMISC_SHIFT |
                                       base_qpn);
        context->mcast = cpu_to_be32(m_promisc << SET_PORT_MC_PROMISC_SHIFT |
index c94b3426d3551796d758603745d4af6bb01d42dd..f0ee35df4dd7779aa971fec3d7fa7555d9bc058a 100644 (file)
@@ -1117,6 +1117,8 @@ static int mlx4_init_port_info(struct mlx4_dev *dev, int port)
        info->port = port;
        mlx4_init_mac_table(dev, &info->mac_table);
        mlx4_init_vlan_table(dev, &info->vlan_table);
+       info->base_qpn = dev->caps.reserved_qps_base[MLX4_QP_REGION_ETH_ADDR] +
+                       (port - 1) * (1 << log_num_mac);
 
        sprintf(info->dev_name, "mlx4_port%d", port);
        info->port_attr.attr.name = info->dev_name;
index 1f95afda68410ad9794c0425366b6dd9c6aa7663..609e0ec14ceebf495c6ddff49ea2cff09738e2cd 100644 (file)
@@ -258,9 +258,12 @@ void mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, int qpn)
        if (validate_index(dev, table, index))
                goto out;
 
-       table->entries[index] = 0;
-       mlx4_set_port_mac_table(dev, port, table->entries);
-       --table->total;
+       /* Check whether this address has reference count */
+       if (!(--table->refs[index])) {
+               table->entries[index] = 0;
+               mlx4_set_port_mac_table(dev, port, table->entries);
+               --table->total;
+       }
 out:
        mutex_unlock(&table->mutex);
 }
index cd6c2317e29e3bb82f555c10816dc4b18bedf1b7..ed47585a6862b2d49af9e51a3f1ac3cab70ce3f6 100644 (file)
@@ -9201,7 +9201,7 @@ static int __devinit niu_ldg_init(struct niu *np)
 
        first_chan = 0;
        for (i = 0; i < port; i++)
-               first_chan += parent->rxchan_per_port[port];
+               first_chan += parent->rxchan_per_port[i];
        num_chan = parent->rxchan_per_port[port];
 
        for (i = first_chan; i < (first_chan + num_chan); i++) {
@@ -9217,7 +9217,7 @@ static int __devinit niu_ldg_init(struct niu *np)
 
        first_chan = 0;
        for (i = 0; i < port; i++)
-               first_chan += parent->txchan_per_port[port];
+               first_chan += parent->txchan_per_port[i];
        num_chan = parent->txchan_per_port[port];
        for (i = first_chan; i < (first_chan + num_chan); i++) {
                err = niu_ldg_assign_ldn(np, parent,
index 7d9c650f395e1399a7185c9338f26b5fd8713db7..02339b3352e7d4340db9106c86b59b61404bf6ea 100644 (file)
@@ -239,6 +239,7 @@ static DEFINE_PCI_DEVICE_TABLE(rtl8169_pci_tbl) = {
        { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8168), 0, 0, RTL_CFG_1 },
        { PCI_DEVICE(PCI_VENDOR_ID_REALTEK,     0x8169), 0, 0, RTL_CFG_0 },
        { PCI_DEVICE(PCI_VENDOR_ID_DLINK,       0x4300), 0, 0, RTL_CFG_0 },
+       { PCI_DEVICE(PCI_VENDOR_ID_DLINK,       0x4302), 0, 0, RTL_CFG_0 },
        { PCI_DEVICE(PCI_VENDOR_ID_AT,          0xc107), 0, 0, RTL_CFG_0 },
        { PCI_DEVICE(0x16ec,                    0x0116), 0, 0, RTL_CFG_0 },
        { PCI_VENDOR_ID_LINKSYS,                0x1032,
@@ -1091,6 +1092,21 @@ rtl_w1w0_eri(void __iomem *ioaddr, int addr, u32 mask, u32 p, u32 m, int type)
        rtl_eri_write(ioaddr, addr, mask, (val & ~m) | p, type);
 }
 
+struct exgmac_reg {
+       u16 addr;
+       u16 mask;
+       u32 val;
+};
+
+static void rtl_write_exgmac_batch(void __iomem *ioaddr,
+                                  const struct exgmac_reg *r, int len)
+{
+       while (len-- > 0) {
+               rtl_eri_write(ioaddr, r->addr, r->mask, r->val, ERIAR_EXGMAC);
+               r++;
+       }
+}
+
 static u8 rtl8168d_efuse_read(void __iomem *ioaddr, int reg_addr)
 {
        u8 value = 0xff;
@@ -3116,6 +3132,18 @@ static void rtl_rar_set(struct rtl8169_private *tp, u8 *addr)
        RTL_W32(MAC0, low);
        RTL_R32(MAC0);
 
+       if (tp->mac_version == RTL_GIGA_MAC_VER_34) {
+               const struct exgmac_reg e[] = {
+                       { .addr = 0xe0, ERIAR_MASK_1111, .val = low },
+                       { .addr = 0xe4, ERIAR_MASK_1111, .val = high },
+                       { .addr = 0xf0, ERIAR_MASK_1111, .val = low << 16 },
+                       { .addr = 0xf4, ERIAR_MASK_1111, .val = high << 16 |
+                                                               low  >> 16 },
+               };
+
+               rtl_write_exgmac_batch(ioaddr, e, ARRAY_SIZE(e));
+       }
+
        RTL_W8(Cfg9346, Cfg9346_Lock);
 
        spin_unlock_irq(&tp->lock);
index 8ad7bfbaa3afb07b61136e0284d8fce1fc25bd40..3c0f1312b3914f29dba7660068a923141eb09663 100644 (file)
@@ -1825,6 +1825,16 @@ static int sis190_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                generic_mii_ioctl(&tp->mii_if, if_mii(ifr), cmd, NULL);
 }
 
+static int sis190_mac_addr(struct net_device  *dev, void *p)
+{
+       int rc;
+
+       rc = eth_mac_addr(dev, p);
+       if (!rc)
+               sis190_init_rxfilter(dev);
+       return rc;
+}
+
 static const struct net_device_ops sis190_netdev_ops = {
        .ndo_open               = sis190_open,
        .ndo_stop               = sis190_close,
@@ -1833,7 +1843,7 @@ static const struct net_device_ops sis190_netdev_ops = {
        .ndo_tx_timeout         = sis190_tx_timeout,
        .ndo_set_multicast_list = sis190_set_rx_mode,
        .ndo_change_mtu         = eth_change_mtu,
-       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_set_mac_address    = sis190_mac_addr,
        .ndo_validate_addr      = eth_validate_addr,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller     = sis190_netpoll,
index fd622a66ebbf037e8401243752ebcece4bd2930d..a03336e086d5ec11423a62b492304c2f00769fc3 100644 (file)
@@ -53,7 +53,7 @@
 #include <linux/usb/usbnet.h>
 #include <linux/usb/cdc.h>
 
-#define        DRIVER_VERSION                          "01-June-2011"
+#define        DRIVER_VERSION                          "04-Aug-2011"
 
 /* CDC NCM subclass 3.2.1 */
 #define USB_CDC_NCM_NDP16_LENGTH_MIN           0x10
@@ -163,35 +163,8 @@ cdc_ncm_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
        usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info));
 }
 
-static int
-cdc_ncm_do_request(struct cdc_ncm_ctx *ctx, struct usb_cdc_notification *req,
-                  void *data, u16 flags, u16 *actlen, u16 timeout)
-{
-       int err;
-
-       err = usb_control_msg(ctx->udev, (req->bmRequestType & USB_DIR_IN) ?
-                               usb_rcvctrlpipe(ctx->udev, 0) :
-                               usb_sndctrlpipe(ctx->udev, 0),
-                               req->bNotificationType, req->bmRequestType,
-                               req->wValue,
-                               req->wIndex, data,
-                               req->wLength, timeout);
-
-       if (err < 0) {
-               if (actlen)
-                       *actlen = 0;
-               return err;
-       }
-
-       if (actlen)
-               *actlen = err;
-
-       return 0;
-}
-
 static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
 {
-       struct usb_cdc_notification req;
        u32 val;
        u8 flags;
        u8 iface_no;
@@ -200,14 +173,14 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
 
        iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber;
 
-       req.bmRequestType = USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE;
-       req.bNotificationType = USB_CDC_GET_NTB_PARAMETERS;
-       req.wValue = 0;
-       req.wIndex = cpu_to_le16(iface_no);
-       req.wLength = cpu_to_le16(sizeof(ctx->ncm_parm));
-
-       err = cdc_ncm_do_request(ctx, &req, &ctx->ncm_parm, 0, NULL, 1000);
-       if (err) {
+       err = usb_control_msg(ctx->udev,
+                               usb_rcvctrlpipe(ctx->udev, 0),
+                               USB_CDC_GET_NTB_PARAMETERS,
+                               USB_TYPE_CLASS | USB_DIR_IN
+                                | USB_RECIP_INTERFACE,
+                               0, iface_no, &ctx->ncm_parm,
+                               sizeof(ctx->ncm_parm), 10000);
+       if (err < 0) {
                pr_debug("failed GET_NTB_PARAMETERS\n");
                return 1;
        }
@@ -253,31 +226,26 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
 
        /* inform device about NTB input size changes */
        if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) {
-               req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT |
-                                                       USB_RECIP_INTERFACE;
-               req.bNotificationType = USB_CDC_SET_NTB_INPUT_SIZE;
-               req.wValue = 0;
-               req.wIndex = cpu_to_le16(iface_no);
 
                if (flags & USB_CDC_NCM_NCAP_NTB_INPUT_SIZE) {
                        struct usb_cdc_ncm_ndp_input_size ndp_in_sz;
-
-                       req.wLength = 8;
-                       ndp_in_sz.dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
-                       ndp_in_sz.wNtbInMaxDatagrams =
-                                       cpu_to_le16(CDC_NCM_DPT_DATAGRAMS_MAX);
-                       ndp_in_sz.wReserved = 0;
-                       err = cdc_ncm_do_request(ctx, &req, &ndp_in_sz, 0, NULL,
-                                                                       1000);
+                       err = usb_control_msg(ctx->udev,
+                                       usb_sndctrlpipe(ctx->udev, 0),
+                                       USB_CDC_SET_NTB_INPUT_SIZE,
+                                       USB_TYPE_CLASS | USB_DIR_OUT
+                                        | USB_RECIP_INTERFACE,
+                                       0, iface_no, &ndp_in_sz, 8, 1000);
                } else {
                        __le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
-
-                       req.wLength = 4;
-                       err = cdc_ncm_do_request(ctx, &req, &dwNtbInMaxSize, 0,
-                                                               NULL, 1000);
+                       err = usb_control_msg(ctx->udev,
+                                       usb_sndctrlpipe(ctx->udev, 0),
+                                       USB_CDC_SET_NTB_INPUT_SIZE,
+                                       USB_TYPE_CLASS | USB_DIR_OUT
+                                        | USB_RECIP_INTERFACE,
+                                       0, iface_no, &dwNtbInMaxSize, 4, 1000);
                }
 
-               if (err)
+               if (err < 0)
                        pr_debug("Setting NTB Input Size failed\n");
        }
 
@@ -332,29 +300,24 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
 
        /* set CRC Mode */
        if (flags & USB_CDC_NCM_NCAP_CRC_MODE) {
-               req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT |
-                                                       USB_RECIP_INTERFACE;
-               req.bNotificationType = USB_CDC_SET_CRC_MODE;
-               req.wValue = cpu_to_le16(USB_CDC_NCM_CRC_NOT_APPENDED);
-               req.wIndex = cpu_to_le16(iface_no);
-               req.wLength = 0;
-
-               err = cdc_ncm_do_request(ctx, &req, NULL, 0, NULL, 1000);
-               if (err)
+               err = usb_control_msg(ctx->udev, usb_sndctrlpipe(ctx->udev, 0),
+                               USB_CDC_SET_CRC_MODE,
+                               USB_TYPE_CLASS | USB_DIR_OUT
+                                | USB_RECIP_INTERFACE,
+                               USB_CDC_NCM_CRC_NOT_APPENDED,
+                               iface_no, NULL, 0, 1000);
+               if (err < 0)
                        pr_debug("Setting CRC mode off failed\n");
        }
 
        /* set NTB format, if both formats are supported */
        if (ntb_fmt_supported & USB_CDC_NCM_NTH32_SIGN) {
-               req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT |
-                                                       USB_RECIP_INTERFACE;
-               req.bNotificationType = USB_CDC_SET_NTB_FORMAT;
-               req.wValue = cpu_to_le16(USB_CDC_NCM_NTB16_FORMAT);
-               req.wIndex = cpu_to_le16(iface_no);
-               req.wLength = 0;
-
-               err = cdc_ncm_do_request(ctx, &req, NULL, 0, NULL, 1000);
-               if (err)
+               err = usb_control_msg(ctx->udev, usb_sndctrlpipe(ctx->udev, 0),
+                               USB_CDC_SET_NTB_FORMAT, USB_TYPE_CLASS
+                                | USB_DIR_OUT | USB_RECIP_INTERFACE,
+                               USB_CDC_NCM_NTB16_FORMAT,
+                               iface_no, NULL, 0, 1000);
+               if (err < 0)
                        pr_debug("Setting NTB format to 16-bit failed\n");
        }
 
@@ -364,17 +327,13 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
        if (flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE) {
                __le16 max_datagram_size;
                u16 eth_max_sz = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize);
-
-               req.bmRequestType = USB_TYPE_CLASS | USB_DIR_IN |
-                                                       USB_RECIP_INTERFACE;
-               req.bNotificationType = USB_CDC_GET_MAX_DATAGRAM_SIZE;
-               req.wValue = 0;
-               req.wIndex = cpu_to_le16(iface_no);
-               req.wLength = cpu_to_le16(2);
-
-               err = cdc_ncm_do_request(ctx, &req, &max_datagram_size, 0, NULL,
-                                                                       1000);
-               if (err) {
+               err = usb_control_msg(ctx->udev, usb_rcvctrlpipe(ctx->udev, 0),
+                               USB_CDC_GET_MAX_DATAGRAM_SIZE,
+                               USB_TYPE_CLASS | USB_DIR_IN
+                                | USB_RECIP_INTERFACE,
+                               0, iface_no, &max_datagram_size,
+                               2, 1000);
+               if (err < 0) {
                        pr_debug("GET_MAX_DATAGRAM_SIZE failed, use size=%u\n",
                                                CDC_NCM_MIN_DATAGRAM_SIZE);
                } else {
@@ -395,17 +354,15 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
                                        CDC_NCM_MIN_DATAGRAM_SIZE;
 
                        /* if value changed, update device */
-                       req.bmRequestType = USB_TYPE_CLASS | USB_DIR_OUT |
-                                                       USB_RECIP_INTERFACE;
-                       req.bNotificationType = USB_CDC_SET_MAX_DATAGRAM_SIZE;
-                       req.wValue = 0;
-                       req.wIndex = cpu_to_le16(iface_no);
-                       req.wLength = 2;
-                       max_datagram_size = cpu_to_le16(ctx->max_datagram_size);
-
-                       err = cdc_ncm_do_request(ctx, &req, &max_datagram_size,
-                                                               0, NULL, 1000);
-                       if (err)
+                       err = usb_control_msg(ctx->udev,
+                                               usb_sndctrlpipe(ctx->udev, 0),
+                                               USB_CDC_SET_MAX_DATAGRAM_SIZE,
+                                               USB_TYPE_CLASS | USB_DIR_OUT
+                                                | USB_RECIP_INTERFACE,
+                                               0,
+                                               iface_no, &max_datagram_size,
+                                               2, 1000);
+                       if (err < 0)
                                pr_debug("SET_MAX_DATAGRAM_SIZE failed\n");
                }
 
@@ -671,7 +628,7 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
        u32 rem;
        u32 offset;
        u32 last_offset;
-       u16 n = 0;
+       u16 n = 0, index;
        u8 ready2send = 0;
 
        /* if there is a remaining skb, it gets priority */
@@ -859,8 +816,8 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
                                        cpu_to_le16(sizeof(ctx->tx_ncm.nth16));
        ctx->tx_ncm.nth16.wSequence = cpu_to_le16(ctx->tx_seq);
        ctx->tx_ncm.nth16.wBlockLength = cpu_to_le16(last_offset);
-       ctx->tx_ncm.nth16.wNdpIndex = ALIGN(sizeof(struct usb_cdc_ncm_nth16),
-                                                       ctx->tx_ndp_modulus);
+       index = ALIGN(sizeof(struct usb_cdc_ncm_nth16), ctx->tx_ndp_modulus);
+       ctx->tx_ncm.nth16.wNdpIndex = cpu_to_le16(index);
 
        memcpy(skb_out->data, &(ctx->tx_ncm.nth16), sizeof(ctx->tx_ncm.nth16));
        ctx->tx_seq++;
@@ -873,12 +830,11 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb)
        ctx->tx_ncm.ndp16.wLength = cpu_to_le16(rem);
        ctx->tx_ncm.ndp16.wNextNdpIndex = 0; /* reserved */
 
-       memcpy(((u8 *)skb_out->data) + ctx->tx_ncm.nth16.wNdpIndex,
+       memcpy(((u8 *)skb_out->data) + index,
                                                &(ctx->tx_ncm.ndp16),
                                                sizeof(ctx->tx_ncm.ndp16));
 
-       memcpy(((u8 *)skb_out->data) + ctx->tx_ncm.nth16.wNdpIndex +
-                                       sizeof(ctx->tx_ncm.ndp16),
+       memcpy(((u8 *)skb_out->data) + index + sizeof(ctx->tx_ncm.ndp16),
                                        &(ctx->tx_ncm.dpe16),
                                        (ctx->tx_curr_frame_num + 1) *
                                        sizeof(struct usb_cdc_ncm_dpe16));
index 9ff7c30573b8dddf6ee9a0ce171adccdb5e25c9f..44d9d8d56490c8a5bbe1b1fac4bc74c364ce92c1 100644 (file)
@@ -309,11 +309,7 @@ static void ar9002_hw_configpcipowersave(struct ath_hw *ah,
        u8 i;
        u32 val;
 
-       if (ah->is_pciexpress != true)
-               return;
-
-       /* Do not touch SerDes registers */
-       if (ah->config.pcie_powersave_enable == 2)
+       if (ah->is_pciexpress != true || ah->aspm_enabled != true)
                return;
 
        /* Nothing to do on restore for 11N */
index 8efdec247c022efa5cbd23b88be3886a9f7debc9..ad2bb2bf4e8a8ff86242ee99bd4a92b8dbb395c2 100644 (file)
@@ -519,11 +519,7 @@ static void ar9003_hw_configpcipowersave(struct ath_hw *ah,
                                         int restore,
                                         int power_off)
 {
-       if (ah->is_pciexpress != true)
-               return;
-
-       /* Do not touch SerDes registers */
-       if (ah->config.pcie_powersave_enable == 2)
+       if (ah->is_pciexpress != true || ah->aspm_enabled != true)
                return;
 
        /* Nothing to do on restore for 11N */
index 8006ce0c73577833324c8891bf5a7428df382a23..8dcefe74f4c39ec768bc06979e3c305d086090b3 100644 (file)
@@ -318,6 +318,14 @@ static void ath9k_hw_disablepcie(struct ath_hw *ah)
        REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
 }
 
+static void ath9k_hw_aspm_init(struct ath_hw *ah)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+
+       if (common->bus_ops->aspm_init)
+               common->bus_ops->aspm_init(common);
+}
+
 /* This should work for all families including legacy */
 static bool ath9k_hw_chip_test(struct ath_hw *ah)
 {
@@ -378,7 +386,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
        ah->config.additional_swba_backoff = 0;
        ah->config.ack_6mb = 0x0;
        ah->config.cwm_ignore_extcca = 0;
-       ah->config.pcie_powersave_enable = 0;
        ah->config.pcie_clock_req = 0;
        ah->config.pcie_waen = 0;
        ah->config.analog_shiftreg = 1;
@@ -598,7 +605,7 @@ static int __ath9k_hw_init(struct ath_hw *ah)
 
 
        if (ah->is_pciexpress)
-               ath9k_hw_configpcipowersave(ah, 0, 0);
+               ath9k_hw_aspm_init(ah);
        else
                ath9k_hw_disablepcie(ah);
 
index 6acd0f975ae1ebe568bb93683d4e26555b5e90ed..c79889036ec40be88bbfec9ed81640ee366c91a4 100644 (file)
@@ -219,7 +219,6 @@ struct ath9k_ops_config {
        int additional_swba_backoff;
        int ack_6mb;
        u32 cwm_ignore_extcca;
-       u8 pcie_powersave_enable;
        bool pcieSerDesWrite;
        u8 pcie_clock_req;
        u32 pcie_waen;
@@ -673,6 +672,7 @@ struct ath_hw {
 
        bool sw_mgmt_crypto;
        bool is_pciexpress;
+       bool aspm_enabled;
        bool is_monitoring;
        bool need_an_top2_fixup;
        u16 tx_trig_level;
@@ -874,6 +874,7 @@ struct ath_bus_ops {
        bool (*eeprom_read)(struct ath_common *common, u32 off, u16 *data);
        void (*bt_coex_prep)(struct ath_common *common);
        void (*extn_synch_en)(struct ath_common *common);
+       void (*aspm_init)(struct ath_common *common);
 };
 
 static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah)
index ac5107172f941aff92e5e73b175cf12f25d7d055..aa0ff7e2c922223759c298d69ccce3a39441a69b 100644 (file)
@@ -670,8 +670,10 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band)
 static void ath9k_init_txpower_limits(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath9k_channel *curchan = ah->curchan;
 
+       ah->txchainmask = common->tx_chainmask;
        if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
                ath9k_init_band_txpower(sc, IEEE80211_BAND_2GHZ);
        if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
index 3bad0b2cf9a3f6517525950a3fe55411d8d79fb0..be4ea132981343dde6ebd521c0bd436dc538438c 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/nl80211.h>
 #include <linux/pci.h>
+#include <linux/pci-aspm.h>
 #include <linux/ath9k_platform.h>
 #include "ath9k.h"
 
@@ -115,12 +116,38 @@ static void ath_pci_extn_synch_enable(struct ath_common *common)
        pci_write_config_byte(pdev, sc->sc_ah->caps.pcie_lcr_offset, lnkctl);
 }
 
+static void ath_pci_aspm_init(struct ath_common *common)
+{
+       struct ath_softc *sc = (struct ath_softc *) common->priv;
+       struct ath_hw *ah = sc->sc_ah;
+       struct pci_dev *pdev = to_pci_dev(sc->dev);
+       struct pci_dev *parent;
+       int pos;
+       u8 aspm;
+
+       if (!pci_is_pcie(pdev))
+               return;
+
+       parent = pdev->bus->self;
+       if (WARN_ON(!parent))
+               return;
+
+       pos = pci_pcie_cap(parent);
+       pci_read_config_byte(parent, pos +  PCI_EXP_LNKCTL, &aspm);
+       if (aspm & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1)) {
+               ah->aspm_enabled = true;
+               /* Initialize PCIe PM and SERDES registers. */
+               ath9k_hw_configpcipowersave(ah, 0, 0);
+       }
+}
+
 static const struct ath_bus_ops ath_pci_bus_ops = {
        .ath_bus_type = ATH_PCI,
        .read_cachesize = ath_pci_read_cachesize,
        .eeprom_read = ath_pci_eeprom_read,
        .bt_coex_prep = ath_pci_bt_coex_prep,
        .extn_synch_en = ath_pci_extn_synch_enable,
+       .aspm_init = ath_pci_aspm_init,
 };
 
 static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
index dab67a12d73b7c612af704bc0c7647af016d2760..73fe3cdf796b28555dcc8e044aaccedf936d7071 100644 (file)
@@ -1746,7 +1746,11 @@ int iwl3945_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
                }
 
                memcpy(active_rxon, staging_rxon, sizeof(*active_rxon));
-
+               /*
+                * We do not commit tx power settings while channel changing,
+                * do it now if tx power changed.
+                */
+               iwl_legacy_set_tx_power(priv, priv->tx_power_next, false);
                return 0;
        }
 
index bd4b000733f7c99906b42d56d8805285f955cfc4..ecdc6e557428ee4d777f955408fc8054fd1c82d5 100644 (file)
@@ -1235,7 +1235,12 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *c
 
                memcpy(active_rxon, &ctx->staging, sizeof(*active_rxon));
                iwl_legacy_print_rx_config_cmd(priv, ctx);
-               goto set_tx_power;
+               /*
+                * We do not commit tx power settings while channel changing,
+                * do it now if tx power changed.
+                */
+               iwl_legacy_set_tx_power(priv, priv->tx_power_next, false);
+               return 0;
        }
 
        /* If we are currently associated and the new config requires
@@ -1315,7 +1320,6 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *c
 
        iwl4965_init_sensitivity(priv);
 
-set_tx_power:
        /* If we issue a new RXON command which required a tune then we must
         * send a new TXPOWER command or we won't be able to Tx any frames */
        ret = iwl_legacy_set_tx_power(priv, priv->tx_power_next, true);
index 3eeb12ebe6e9b3e39b6dd9c7a21b6709ff3fb286..c95cefd529dc31d13819b037006495e7798e032f 100644 (file)
@@ -365,6 +365,7 @@ static struct iwl_base_params iwl5000_base_params = {
        .chain_noise_scale = 1000,
        .wd_timeout = IWL_LONG_WD_TIMEOUT,
        .max_event_log_size = 512,
+       .no_idle_support = true,
 };
 static struct iwl_ht_params iwl5000_ht_params = {
        .ht_greenfield_support = true,
index 3e6bb734dcb71a5af93318e44c03240481d1dc9b..02817a43855097e9c12bac9a4aa5a7de2caf9124 100644 (file)
@@ -135,6 +135,7 @@ struct iwl_mod_params {
  * @temperature_kelvin: temperature report by uCode in kelvin
  * @max_event_log_size: size of event log buffer size for ucode event logging
  * @shadow_reg_enable: HW shadhow register bit
+ * @no_idle_support: do not support idle mode
  */
 struct iwl_base_params {
        int eeprom_size;
@@ -156,6 +157,7 @@ struct iwl_base_params {
        bool temperature_kelvin;
        u32 max_event_log_size;
        const bool shadow_reg_enable;
+       const bool no_idle_support;
 };
 /*
  * @advanced_bt_coexist: support advanced bt coexist
index fb7e436b40c708df3413e884d11cb63233eb0796..69d4ec467dca70d8da0dbe0129677f42da2f2d30 100644 (file)
@@ -134,6 +134,7 @@ static void iwl_pci_apm_config(struct iwl_bus *bus)
 static void iwl_pci_set_drv_data(struct iwl_bus *bus, void *drv_data)
 {
        bus->drv_data = drv_data;
+       pci_set_drvdata(IWL_BUS_GET_PCI_DEV(bus), drv_data);
 }
 
 static void iwl_pci_get_hw_id(struct iwl_bus *bus, char buf[],
@@ -454,8 +455,6 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                pci_write_config_word(pdev, PCI_COMMAND, pci_cmd);
        }
 
-       pci_set_drvdata(pdev, bus);
-
        bus->dev = &pdev->dev;
        bus->irq = pdev->irq;
        bus->ops = &pci_ops;
@@ -494,11 +493,12 @@ static void iwl_pci_down(struct iwl_bus *bus)
 
 static void __devexit iwl_pci_remove(struct pci_dev *pdev)
 {
-       struct iwl_bus *bus = pci_get_drvdata(pdev);
+       struct iwl_priv *priv = pci_get_drvdata(pdev);
+       void *bus_specific = priv->bus->bus_specific;
 
-       iwl_remove(bus->drv_data);
+       iwl_remove(priv);
 
-       iwl_pci_down(bus);
+       iwl_pci_down(bus_specific);
 }
 
 #ifdef CONFIG_PM
@@ -506,20 +506,20 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev)
 static int iwl_pci_suspend(struct device *device)
 {
        struct pci_dev *pdev = to_pci_dev(device);
-       struct iwl_bus *bus = pci_get_drvdata(pdev);
+       struct iwl_priv *priv = pci_get_drvdata(pdev);
 
        /* Before you put code here, think about WoWLAN. You cannot check here
         * whether WoWLAN is enabled or not, and your code will run even if
         * WoWLAN is enabled - don't kill the NIC, someone may need it in Sx.
         */
 
-       return iwl_suspend(bus->drv_data);
+       return iwl_suspend(priv);
 }
 
 static int iwl_pci_resume(struct device *device)
 {
        struct pci_dev *pdev = to_pci_dev(device);
-       struct iwl_bus *bus = pci_get_drvdata(pdev);
+       struct iwl_priv *priv = pci_get_drvdata(pdev);
 
        /* Before you put code here, think about WoWLAN. You cannot check here
         * whether WoWLAN is enabled or not, and your code will run even if
@@ -532,7 +532,7 @@ static int iwl_pci_resume(struct device *device)
         */
        pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
 
-       return iwl_resume(bus->drv_data);
+       return iwl_resume(priv);
 }
 
 static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume);
index 3ec619c6881c06f408e00e9896539734309fb5c5..cd64df05f9ed7442c47bbffff722666e36808df1 100644 (file)
@@ -349,7 +349,8 @@ static void iwl_power_build_cmd(struct iwl_priv *priv,
 
        if (priv->wowlan)
                iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper);
-       else if (priv->hw->conf.flags & IEEE80211_CONF_IDLE)
+       else if (!priv->cfg->base_params->no_idle_support &&
+                priv->hw->conf.flags & IEEE80211_CONF_IDLE)
                iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20);
        else if (iwl_tt_is_low_power_state(priv)) {
                /* in thermal throttling low power state */
index 84ab7d1acb6a5a71c7f307a978b2a0036a3e27fb..ef67f6786a84785bc00c629789d25011ca6148ce 100644 (file)
@@ -703,8 +703,7 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
        /*
         * Add space for the TXWI in front of the skb.
         */
-       skb_push(entry->skb, TXWI_DESC_SIZE);
-       memset(entry->skb, 0, TXWI_DESC_SIZE);
+       memset(skb_push(entry->skb, TXWI_DESC_SIZE), 0, TXWI_DESC_SIZE);
 
        /*
         * Register descriptor details in skb frame descriptor.
index 15cdc7e57fc4711cbdc7bca15ae24820afff53d8..4cdf247a870d6e21dbc5557e1e1aad18fa4253c7 100644 (file)
@@ -355,7 +355,8 @@ static inline enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *
        return CIPHER_NONE;
 }
 
-static inline void rt2x00crypto_create_tx_descriptor(struct queue_entry *entry,
+static inline void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev,
+                                                    struct sk_buff *skb,
                                                     struct txentry_desc *txdesc)
 {
 }
index 8efab3983528a0e54d0f70bdeb96e2729c414c08..4ccf238059730cf20e589d9f567e35a3af3f189d 100644 (file)
@@ -113,7 +113,7 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
         * due to possible race conditions in mac80211.
         */
        if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
-               goto exit_fail;
+               goto exit_free_skb;
 
        /*
         * Use the ATIM queue if appropriate and present.
@@ -127,7 +127,7 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
                ERROR(rt2x00dev,
                      "Attempt to send packet over invalid queue %d.\n"
                      "Please file bug report to %s.\n", qid, DRV_PROJECT);
-               goto exit_fail;
+               goto exit_free_skb;
        }
 
        /*
@@ -159,6 +159,7 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 
  exit_fail:
        rt2x00queue_pause_queue(queue);
+ exit_free_skb:
        dev_kfree_skb_any(skb);
 }
 EXPORT_SYMBOL_GPL(rt2x00mac_tx);
index 5efd578334897bbf451cc0ba33b17e060e50c089..56f12358389d8614ba9167ad23239808cc489905 100644 (file)
@@ -1696,15 +1696,17 @@ static bool _rtl_pci_find_adapter(struct pci_dev *pdev,
        pcipriv->ndis_adapter.devnumber = PCI_SLOT(pdev->devfn);
        pcipriv->ndis_adapter.funcnumber = PCI_FUNC(pdev->devfn);
 
-       /*find bridge info */
-       pcipriv->ndis_adapter.pcibridge_vendorid = bridge_pdev->vendor;
-       for (tmp = 0; tmp < PCI_BRIDGE_VENDOR_MAX; tmp++) {
-               if (bridge_pdev->vendor == pcibridge_vendors[tmp]) {
-                       pcipriv->ndis_adapter.pcibridge_vendor = tmp;
-                       RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
-                                ("Pci Bridge Vendor is found index: %d\n",
-                                 tmp));
-                       break;
+       if (bridge_pdev) {
+               /*find bridge info if available */
+               pcipriv->ndis_adapter.pcibridge_vendorid = bridge_pdev->vendor;
+               for (tmp = 0; tmp < PCI_BRIDGE_VENDOR_MAX; tmp++) {
+                       if (bridge_pdev->vendor == pcibridge_vendors[tmp]) {
+                               pcipriv->ndis_adapter.pcibridge_vendor = tmp;
+                               RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG,
+                                        ("Pci Bridge Vendor is found index:"
+                                        " %d\n", tmp));
+                               break;
+                       }
                }
        }
 
index da1f4b9605df3ed032b559bc7623ba780aaba8c3..72c33fbe451d6c31f1b50d0f38ced362d5258210 100644 (file)
@@ -610,6 +610,6 @@ void __iomem *of_iomap(struct device_node *np, int index)
        if (of_address_to_resource(np, index, &res))
                return NULL;
 
-       return ioremap(res.start, 1 + res.end - res.start);
+       return ioremap(res.start, resource_size(&res));
 }
 EXPORT_SYMBOL(of_iomap);
index 02ed36719defce57fb30f8182a02903ad2b16445..3ff22e32b6020c5f325a1e25f97c68ea84ee3221 100644 (file)
@@ -610,8 +610,9 @@ EXPORT_SYMBOL(of_find_node_by_phandle);
  *
  * The out_value is modified only if a valid u32 value can be decoded.
  */
-int of_property_read_u32_array(const struct device_node *np, char *propname,
-                              u32 *out_values, size_t sz)
+int of_property_read_u32_array(const struct device_node *np,
+                              const char *propname, u32 *out_values,
+                              size_t sz)
 {
        struct property *prop = of_find_property(np, propname, NULL);
        const __be32 *val;
@@ -645,7 +646,7 @@ EXPORT_SYMBOL_GPL(of_property_read_u32_array);
  *
  * The out_string pointer is modified only if a valid string can be decoded.
  */
-int of_property_read_string(struct device_node *np, char *propname,
+int of_property_read_string(struct device_node *np, const char *propname,
                                const char **out_string)
 {
        struct property *prop = of_find_property(np, propname, NULL);
index a70fa89f76fdcbf20407747849ce75409f27f0c1..220285760b68691f2d3d16ec79822de2383a50be 100644 (file)
@@ -110,7 +110,7 @@ static int post_dock_fixups(struct notifier_block *nb, unsigned long val,
 }
 
 
-static struct acpi_dock_ops acpiphp_dock_ops = {
+static const struct acpi_dock_ops acpiphp_dock_ops = {
        .handler = handle_hotplug_event_func,
 };
 
index 45e0191c35ddff9f977af6d07a6876e47e0f1bce..1e88d478532156de395f609d2a750db792221164 100644 (file)
@@ -769,4 +769,12 @@ config INTEL_OAKTRAIL
          enable/disable the Camera, WiFi, BT etc. devices. If in doubt, say Y
          here; it will only load on supported platforms.
 
+config SAMSUNG_Q10
+       tristate "Samsung Q10 Extras"
+       depends on SERIO_I8042
+       select BACKLIGHT_CLASS_DEVICE
+       ---help---
+         This driver provides support for backlight control on Samsung Q10
+         and related laptops, including Dell Latitude X200.
+
 endif # X86_PLATFORM_DEVICES
index afc1f832aa67df8639131835bbd2416bb5ac7774..293a320d9faaec9003fff3fa402d3e73322c66ae 100644 (file)
@@ -44,3 +44,4 @@ obj-$(CONFIG_SAMSUNG_LAPTOP)  += samsung-laptop.o
 obj-$(CONFIG_MXM_WMI)          += mxm-wmi.o
 obj-$(CONFIG_INTEL_MID_POWER_BUTTON)   += intel_mid_powerbtn.o
 obj-$(CONFIG_INTEL_OAKTRAIL)   += intel_oaktrail.o
+obj-$(CONFIG_SAMSUNG_Q10)      += samsung-q10.o
index e1c4938b301bbcdacbcc1303e9b8cde5bf288585..af2bb20cb2fbd39bcc3be8d77e42565cb35b1877 100644 (file)
@@ -99,6 +99,7 @@ enum acer_wmi_event_ids {
 static const struct key_entry acer_wmi_keymap[] = {
        {KE_KEY, 0x01, {KEY_WLAN} },     /* WiFi */
        {KE_KEY, 0x03, {KEY_WLAN} },     /* WiFi */
+       {KE_KEY, 0x04, {KEY_WLAN} },     /* WiFi */
        {KE_KEY, 0x12, {KEY_BLUETOOTH} },       /* BT */
        {KE_KEY, 0x21, {KEY_PROG1} },    /* Backup */
        {KE_KEY, 0x22, {KEY_PROG2} },    /* Arcade */
@@ -304,6 +305,10 @@ static struct quirk_entry quirk_fujitsu_amilo_li_1718 = {
        .wireless = 2,
 };
 
+static struct quirk_entry quirk_lenovo_ideapad_s205 = {
+       .wireless = 3,
+};
+
 /* The Aspire One has a dummy ACPI-WMI interface - disable it */
 static struct dmi_system_id __devinitdata acer_blacklist[] = {
        {
@@ -450,6 +455,15 @@ static struct dmi_system_id acer_quirks[] = {
                },
                .driver_data = &quirk_medion_md_98300,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "Lenovo Ideapad S205",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "10382LG"),
+               },
+               .driver_data = &quirk_lenovo_ideapad_s205,
+       },
        {}
 };
 
@@ -542,6 +556,12 @@ struct wmi_interface *iface)
                                return AE_ERROR;
                        *value = result & 0x1;
                        return AE_OK;
+               case 3:
+                       err = ec_read(0x78, &result);
+                       if (err)
+                               return AE_ERROR;
+                       *value = result & 0x1;
+                       return AE_OK;
                default:
                        err = ec_read(0xA, &result);
                        if (err)
@@ -1266,8 +1286,13 @@ static void acer_rfkill_update(struct work_struct *ignored)
        acpi_status status;
 
        status = get_u32(&state, ACER_CAP_WIRELESS);
-       if (ACPI_SUCCESS(status))
-               rfkill_set_sw_state(wireless_rfkill, !state);
+       if (ACPI_SUCCESS(status)) {
+               if (quirks->wireless == 3) {
+                       rfkill_set_hw_state(wireless_rfkill, !state);
+               } else {
+                       rfkill_set_sw_state(wireless_rfkill, !state);
+               }
+       }
 
        if (has_cap(ACER_CAP_BLUETOOTH)) {
                status = get_u32(&state, ACER_CAP_BLUETOOTH);
@@ -1400,6 +1425,9 @@ static ssize_t show_bool_threeg(struct device *dev,
 {
        u32 result; \
        acpi_status status;
+
+       pr_info("This threeg sysfs will be removed in 2012"
+               " - used by: %s\n", current->comm);
        if (wmi_has_guid(WMID_GUID3))
                status = wmid3_get_device_status(&result,
                                ACER_WMID3_GDS_THREEG);
@@ -1415,8 +1443,10 @@ static ssize_t set_bool_threeg(struct device *dev,
 {
        u32 tmp = simple_strtoul(buf, NULL, 10);
        acpi_status status = set_u32(tmp, ACER_CAP_THREEG);
-               if (ACPI_FAILURE(status))
-                       return -EINVAL;
+       pr_info("This threeg sysfs will be removed in 2012"
+               " - used by: %s\n", current->comm);
+       if (ACPI_FAILURE(status))
+               return -EINVAL;
        return count;
 }
 static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg,
@@ -1425,6 +1455,8 @@ static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg,
 static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
        char *buf)
 {
+       pr_info("This interface sysfs will be removed in 2012"
+               " - used by: %s\n", current->comm);
        switch (interface->type) {
        case ACER_AMW0:
                return sprintf(buf, "AMW0\n");
index fca3489218b76b0c48a08fb5ef2cc0e0ad3250ed..760c6d7624fe505f5da3f40060a1df9d0eaf12b0 100644 (file)
@@ -182,6 +182,7 @@ static const struct bios_settings_t bios_tbl[] = {
        {"Acer", "Aspire 1810T",  "v1.3308", 0x55, 0x58, {0x9e, 0x00} },
        {"Acer", "Aspire 1810TZ", "v1.3310", 0x55, 0x58, {0x9e, 0x00} },
        {"Acer", "Aspire 1810T",  "v1.3310", 0x55, 0x58, {0x9e, 0x00} },
+       {"Acer", "Aspire 1810TZ", "v1.3314", 0x55, 0x58, {0x9e, 0x00} },
        /* Acer 531 */
        {"Acer", "AO531h", "v0.3201", 0x55, 0x58, {0x20, 0x00} },
        /* Gateway */
@@ -703,15 +704,15 @@ MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Peter Feuerer");
 MODULE_DESCRIPTION("Aspire One temperature and fan driver");
 MODULE_ALIAS("dmi:*:*Acer*:pnAOA*:");
-MODULE_ALIAS("dmi:*:*Acer*:pnAspire 1410*:");
-MODULE_ALIAS("dmi:*:*Acer*:pnAspire 1810*:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1410*:");
+MODULE_ALIAS("dmi:*:*Acer*:pnAspire*1810*:");
 MODULE_ALIAS("dmi:*:*Acer*:pnAO531*:");
 MODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:");
 MODULE_ALIAS("dmi:*:*Gateway*:pnLT31*:");
-MODULE_ALIAS("dmi:*:*Packard Bell*:pnAOA*:");
-MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOA*:");
-MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOTMU*:");
-MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOTMA*:");
+MODULE_ALIAS("dmi:*:*Packard*Bell*:pnAOA*:");
+MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOA*:");
+MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMU*:");
+MODULE_ALIAS("dmi:*:*Packard*Bell*:pnDOTMA*:");
 
 module_init(acerhdf_init);
 module_exit(acerhdf_exit);
index d65df92e2acc63fb9eea85d82434c359ed8654fd..fa6d7ec68b269670b4de8ca24cb97b3d55160acc 100644 (file)
@@ -70,11 +70,10 @@ MODULE_LICENSE("GPL");
  * WAPF defines the behavior of the Fn+Fx wlan key
  * The significance of values is yet to be found, but
  * most of the time:
- * 0x0 will do nothing
- * 0x1 will allow to control the device with Fn+Fx key.
- * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key
- * 0x5 like 0x1 or 0x4
- * So, if something doesn't work as you want, just try other values =)
+ * Bit | Bluetooth | WLAN
+ *  0  | Hardware  | Hardware
+ *  1  | Hardware  | Software
+ *  4  | Software  | Software
  */
 static uint wapf = 1;
 module_param(wapf, uint, 0444);
index 0580d99b0798e9bf68f03cd8877bf07a141dff94..b0859d4183e8307d27ef9c3979bf613d4548c603 100644 (file)
@@ -38,6 +38,24 @@ MODULE_LICENSE("GPL");
 
 MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
 
+/*
+ * WAPF defines the behavior of the Fn+Fx wlan key
+ * The significance of values is yet to be found, but
+ * most of the time:
+ * Bit | Bluetooth | WLAN
+ *  0  | Hardware  | Hardware
+ *  1  | Hardware  | Software
+ *  4  | Software  | Software
+ */
+static uint wapf;
+module_param(wapf, uint, 0444);
+MODULE_PARM_DESC(wapf, "WAPF value");
+
+static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
+{
+       driver->wapf = wapf;
+}
+
 static const struct key_entry asus_nb_wmi_keymap[] = {
        { KE_KEY, 0x30, { KEY_VOLUMEUP } },
        { KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
@@ -53,16 +71,16 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
        { KE_KEY, 0x51, { KEY_WWW } },
        { KE_KEY, 0x55, { KEY_CALC } },
        { KE_KEY, 0x5C, { KEY_F15 } },  /* Power Gear key */
-       { KE_KEY, 0x5D, { KEY_WLAN } },
-       { KE_KEY, 0x5E, { KEY_WLAN } },
-       { KE_KEY, 0x5F, { KEY_WLAN } },
+       { KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */
+       { KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */
+       { KE_KEY, 0x5F, { KEY_WLAN } }, /* Wireless console Disable */
        { KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } },
        { KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } },
        { KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } },
        { KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } },
        { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
-       { KE_KEY, 0x7E, { KEY_BLUETOOTH } },
        { KE_KEY, 0x7D, { KEY_BLUETOOTH } },
+       { KE_KEY, 0x7E, { KEY_BLUETOOTH } },
        { KE_KEY, 0x82, { KEY_CAMERA } },
        { KE_KEY, 0x88, { KEY_RFKILL  } },
        { KE_KEY, 0x8A, { KEY_PROG1 } },
@@ -81,6 +99,7 @@ static struct asus_wmi_driver asus_nb_wmi_driver = {
        .keymap = asus_nb_wmi_keymap,
        .input_name = "Asus WMI hotkeys",
        .input_phys = ASUS_NB_WMI_FILE "/input0",
+       .quirks = asus_nb_wmi_quirks,
 };
 
 
index 65b66aa44c786f64dba15a94d607b292260630e7..95cba9ebf6c08ddab129840447367fb0e83aea9e 100644 (file)
@@ -44,6 +44,7 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/platform_device.h>
+#include <linux/thermal.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 
@@ -66,6 +67,8 @@ MODULE_LICENSE("GPL");
 #define NOTIFY_BRNUP_MAX               0x1f
 #define NOTIFY_BRNDOWN_MIN             0x20
 #define NOTIFY_BRNDOWN_MAX             0x2e
+#define NOTIFY_KBD_BRTUP               0xc4
+#define NOTIFY_KBD_BRTDWN              0xc5
 
 /* WMI Methods */
 #define ASUS_WMI_METHODID_SPEC         0x43455053 /* BIOS SPECification */
@@ -93,6 +96,7 @@ MODULE_LICENSE("GPL");
 /* Wireless */
 #define ASUS_WMI_DEVID_HW_SWITCH       0x00010001
 #define ASUS_WMI_DEVID_WIRELESS_LED    0x00010002
+#define ASUS_WMI_DEVID_CWAP            0x00010003
 #define ASUS_WMI_DEVID_WLAN            0x00010011
 #define ASUS_WMI_DEVID_BLUETOOTH       0x00010013
 #define ASUS_WMI_DEVID_GPS             0x00010015
@@ -102,6 +106,12 @@ MODULE_LICENSE("GPL");
 
 /* Leds */
 /* 0x000200XX and 0x000400XX */
+#define ASUS_WMI_DEVID_LED1            0x00020011
+#define ASUS_WMI_DEVID_LED2            0x00020012
+#define ASUS_WMI_DEVID_LED3            0x00020013
+#define ASUS_WMI_DEVID_LED4            0x00020014
+#define ASUS_WMI_DEVID_LED5            0x00020015
+#define ASUS_WMI_DEVID_LED6            0x00020016
 
 /* Backlight and Brightness */
 #define ASUS_WMI_DEVID_BACKLIGHT       0x00050011
@@ -174,13 +184,18 @@ struct asus_wmi {
 
        struct led_classdev tpd_led;
        int tpd_led_wk;
+       struct led_classdev kbd_led;
+       int kbd_led_wk;
        struct workqueue_struct *led_workqueue;
        struct work_struct tpd_led_work;
+       struct work_struct kbd_led_work;
 
        struct asus_rfkill wlan;
        struct asus_rfkill bluetooth;
        struct asus_rfkill wimax;
        struct asus_rfkill wwan3g;
+       struct asus_rfkill gps;
+       struct asus_rfkill uwb;
 
        struct hotplug_slot *hotplug_slot;
        struct mutex hotplug_lock;
@@ -205,6 +220,7 @@ static int asus_wmi_input_init(struct asus_wmi *asus)
        asus->inputdev->phys = asus->driver->input_phys;
        asus->inputdev->id.bustype = BUS_HOST;
        asus->inputdev->dev.parent = &asus->platform_device->dev;
+       set_bit(EV_REP, asus->inputdev->evbit);
 
        err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL);
        if (err)
@@ -359,30 +375,80 @@ static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
        return read_tpd_led_state(asus);
 }
 
-static int asus_wmi_led_init(struct asus_wmi *asus)
+static void kbd_led_update(struct work_struct *work)
 {
-       int rv;
+       int ctrl_param = 0;
+       struct asus_wmi *asus;
 
-       if (read_tpd_led_state(asus) < 0)
-               return 0;
+       asus = container_of(work, struct asus_wmi, kbd_led_work);
 
-       asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
-       if (!asus->led_workqueue)
-               return -ENOMEM;
-       INIT_WORK(&asus->tpd_led_work, tpd_led_update);
+       /*
+        * bits 0-2: level
+        * bit 7: light on/off
+        */
+       if (asus->kbd_led_wk > 0)
+               ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
 
-       asus->tpd_led.name = "asus::touchpad";
-       asus->tpd_led.brightness_set = tpd_led_set;
-       asus->tpd_led.brightness_get = tpd_led_get;
-       asus->tpd_led.max_brightness = 1;
+       asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
+}
 
-       rv = led_classdev_register(&asus->platform_device->dev, &asus->tpd_led);
-       if (rv) {
-               destroy_workqueue(asus->led_workqueue);
-               return rv;
+static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
+{
+       int retval;
+
+       /*
+        * bits 0-2: level
+        * bit 7: light on/off
+        * bit 8-10: environment (0: dark, 1: normal, 2: light)
+        * bit 17: status unknown
+        */
+       retval = asus_wmi_get_devstate_bits(asus, ASUS_WMI_DEVID_KBD_BACKLIGHT,
+                                           0xFFFF);
+
+       /* Unknown status is considered as off */
+       if (retval == 0x8000)
+               retval = 0;
+
+       if (retval >= 0) {
+               if (level)
+                       *level = retval & 0x80 ? retval & 0x7F : 0;
+               if (env)
+                       *env = (retval >> 8) & 0x7F;
+               retval = 0;
        }
 
-       return 0;
+       return retval;
+}
+
+static void kbd_led_set(struct led_classdev *led_cdev,
+                       enum led_brightness value)
+{
+       struct asus_wmi *asus;
+
+       asus = container_of(led_cdev, struct asus_wmi, kbd_led);
+
+       if (value > asus->kbd_led.max_brightness)
+               value = asus->kbd_led.max_brightness;
+       else if (value < 0)
+               value = 0;
+
+       asus->kbd_led_wk = value;
+       queue_work(asus->led_workqueue, &asus->kbd_led_work);
+}
+
+static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
+{
+       struct asus_wmi *asus;
+       int retval, value;
+
+       asus = container_of(led_cdev, struct asus_wmi, kbd_led);
+
+       retval = kbd_led_read(asus, &value, NULL);
+
+       if (retval < 0)
+               return retval;
+
+       return value;
 }
 
 static void asus_wmi_led_exit(struct asus_wmi *asus)
@@ -393,6 +459,48 @@ static void asus_wmi_led_exit(struct asus_wmi *asus)
                destroy_workqueue(asus->led_workqueue);
 }
 
+static int asus_wmi_led_init(struct asus_wmi *asus)
+{
+       int rv = 0;
+
+       asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
+       if (!asus->led_workqueue)
+               return -ENOMEM;
+
+       if (read_tpd_led_state(asus) >= 0) {
+               INIT_WORK(&asus->tpd_led_work, tpd_led_update);
+
+               asus->tpd_led.name = "asus::touchpad";
+               asus->tpd_led.brightness_set = tpd_led_set;
+               asus->tpd_led.brightness_get = tpd_led_get;
+               asus->tpd_led.max_brightness = 1;
+
+               rv = led_classdev_register(&asus->platform_device->dev,
+                                          &asus->tpd_led);
+               if (rv)
+                       goto error;
+       }
+
+       if (kbd_led_read(asus, NULL, NULL) >= 0) {
+               INIT_WORK(&asus->kbd_led_work, kbd_led_update);
+
+               asus->kbd_led.name = "asus::kbd_backlight";
+               asus->kbd_led.brightness_set = kbd_led_set;
+               asus->kbd_led.brightness_get = kbd_led_get;
+               asus->kbd_led.max_brightness = 3;
+
+               rv = led_classdev_register(&asus->platform_device->dev,
+                                          &asus->kbd_led);
+       }
+
+error:
+       if (rv)
+               asus_wmi_led_exit(asus);
+
+       return rv;
+}
+
+
 /*
  * PCI hotplug (for wlan rfkill)
  */
@@ -729,6 +837,16 @@ static void asus_wmi_rfkill_exit(struct asus_wmi *asus)
                rfkill_destroy(asus->wwan3g.rfkill);
                asus->wwan3g.rfkill = NULL;
        }
+       if (asus->gps.rfkill) {
+               rfkill_unregister(asus->gps.rfkill);
+               rfkill_destroy(asus->gps.rfkill);
+               asus->gps.rfkill = NULL;
+       }
+       if (asus->uwb.rfkill) {
+               rfkill_unregister(asus->uwb.rfkill);
+               rfkill_destroy(asus->uwb.rfkill);
+               asus->uwb.rfkill = NULL;
+       }
 }
 
 static int asus_wmi_rfkill_init(struct asus_wmi *asus)
@@ -763,6 +881,18 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus)
        if (result && result != -ENODEV)
                goto exit;
 
+       result = asus_new_rfkill(asus, &asus->gps, "asus-gps",
+                                RFKILL_TYPE_GPS, ASUS_WMI_DEVID_GPS);
+
+       if (result && result != -ENODEV)
+               goto exit;
+
+       result = asus_new_rfkill(asus, &asus->uwb, "asus-uwb",
+                                RFKILL_TYPE_UWB, ASUS_WMI_DEVID_UWB);
+
+       if (result && result != -ENODEV)
+               goto exit;
+
        if (!asus->driver->hotplug_wireless)
                goto exit;
 
@@ -797,8 +927,8 @@ exit:
  * Hwmon device
  */
 static ssize_t asus_hwmon_pwm1(struct device *dev,
-                           struct device_attribute *attr,
-                           char *buf)
+                              struct device_attribute *attr,
+                              char *buf)
 {
        struct asus_wmi *asus = dev_get_drvdata(dev);
        u32 value;
@@ -809,7 +939,7 @@ static ssize_t asus_hwmon_pwm1(struct device *dev,
        if (err < 0)
                return err;
 
-       value |= 0xFF;
+       value &= 0xFF;
 
        if (value == 1) /* Low Speed */
                value = 85;
@@ -825,7 +955,26 @@ static ssize_t asus_hwmon_pwm1(struct device *dev,
        return sprintf(buf, "%d\n", value);
 }
 
+static ssize_t asus_hwmon_temp1(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct asus_wmi *asus = dev_get_drvdata(dev);
+       u32 value;
+       int err;
+
+       err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, &value);
+
+       if (err < 0)
+               return err;
+
+       value = KELVIN_TO_CELSIUS((value & 0xFFFF)) * 1000;
+
+       return sprintf(buf, "%d\n", value);
+}
+
 static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO, asus_hwmon_pwm1, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL, 0);
 
 static ssize_t
 show_name(struct device *dev, struct device_attribute *attr, char *buf)
@@ -836,12 +985,13 @@ static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
 
 static struct attribute *hwmon_attributes[] = {
        &sensor_dev_attr_pwm1.dev_attr.attr,
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
        &sensor_dev_attr_name.dev_attr.attr,
        NULL
 };
 
 static mode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
-                                   struct attribute *attr, int idx)
+                                         struct attribute *attr, int idx)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
        struct platform_device *pdev = to_platform_device(dev->parent);
@@ -852,6 +1002,8 @@ static mode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
 
        if (attr == &sensor_dev_attr_pwm1.dev_attr.attr)
                dev_id = ASUS_WMI_DEVID_FAN_CTRL;
+       else if (attr == &sensor_dev_attr_temp1_input.dev_attr.attr)
+               dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
 
        if (dev_id != -1) {
                int err = asus_wmi_get_devstate(asus, dev_id, &value);
@@ -869,9 +1021,13 @@ static mode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
                 * - reverved bits are non-zero
                 * - sfun and presence bit are not set
                 */
-               if (value != ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000
+               if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000
                    || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)))
                        ok = false;
+       } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
+               /* If value is zero, something is clearly wrong */
+               if (value == 0)
+                       ok = false;
        }
 
        return ok ? attr->mode : 0;
@@ -904,6 +1060,7 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus)
                pr_err("Could not register asus hwmon device\n");
                return PTR_ERR(hwmon);
        }
+       dev_set_drvdata(hwmon, asus);
        asus->hwmon_device = hwmon;
        result = sysfs_create_group(&hwmon->kobj, &hwmon_attribute_group);
        if (result)
@@ -1060,6 +1217,8 @@ static void asus_wmi_notify(u32 value, void *context)
        acpi_status status;
        int code;
        int orig_code;
+       unsigned int key_value = 1;
+       bool autorelease = 1;
 
        status = wmi_get_event_data(value, &response);
        if (status != AE_OK) {
@@ -1075,6 +1234,13 @@ static void asus_wmi_notify(u32 value, void *context)
        code = obj->integer.value;
        orig_code = code;
 
+       if (asus->driver->key_filter) {
+               asus->driver->key_filter(asus->driver, &code, &key_value,
+                                        &autorelease);
+               if (code == ASUS_WMI_KEY_IGNORE)
+                       goto exit;
+       }
+
        if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
                code = NOTIFY_BRNUP_MIN;
        else if (code >= NOTIFY_BRNDOWN_MIN &&
@@ -1084,7 +1250,8 @@ static void asus_wmi_notify(u32 value, void *context)
        if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
                if (!acpi_video_backlight_support())
                        asus_wmi_backlight_notify(asus, orig_code);
-       } else if (!sparse_keymap_report_event(asus->inputdev, code, 1, true))
+       } else if (!sparse_keymap_report_event(asus->inputdev, code,
+                                              key_value, autorelease))
                pr_info("Unknown key %x pressed\n", code);
 
 exit:
@@ -1164,14 +1331,18 @@ ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER);
 static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
                           const char *buf, size_t count)
 {
-       int value;
+       int value, rv;
 
        if (!count || sscanf(buf, "%i", &value) != 1)
                return -EINVAL;
        if (value < 0 || value > 2)
                return -EINVAL;
 
-       return asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL);
+       rv = asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL);
+       if (rv < 0)
+               return rv;
+
+       return count;
 }
 
 static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
@@ -1234,7 +1405,7 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
 
        /* We don't know yet what to do with this version... */
        if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) {
-               pr_info("BIOS WMI version: %d.%d", rv >> 8, rv & 0xFF);
+               pr_info("BIOS WMI version: %d.%d", rv >> 16, rv & 0xFF);
                asus->spec = rv;
        }
 
@@ -1266,6 +1437,12 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
                return -ENODEV;
        }
 
+       /* CWAP allow to define the behavior of the Fn+F2 key,
+        * this method doesn't seems to be present on Eee PCs */
+       if (asus->driver->wapf >= 0)
+               asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
+                                     asus->driver->wapf, NULL);
+
        return asus_wmi_sysfs_init(asus->platform_device);
 }
 
@@ -1568,6 +1745,14 @@ static int asus_hotk_restore(struct device *device)
                bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WWAN3G);
                rfkill_set_sw_state(asus->wwan3g.rfkill, bl);
        }
+       if (asus->gps.rfkill) {
+               bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_GPS);
+               rfkill_set_sw_state(asus->gps.rfkill, bl);
+       }
+       if (asus->uwb.rfkill) {
+               bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_UWB);
+               rfkill_set_sw_state(asus->uwb.rfkill, bl);
+       }
 
        return 0;
 }
@@ -1604,7 +1789,7 @@ static int asus_wmi_probe(struct platform_device *pdev)
 
 static bool used;
 
-int asus_wmi_register_driver(struct asus_wmi_driver *driver)
+int __init_or_module asus_wmi_register_driver(struct asus_wmi_driver *driver)
 {
        struct platform_driver *platform_driver;
        struct platform_device *platform_device;
index c044522c87661ad59d38ab78d4d4931bde97d5dc..8147c10161cc590d729c2afcfc03c43df7df3e1f 100644 (file)
 
 #include <linux/platform_device.h>
 
+#define ASUS_WMI_KEY_IGNORE (-1)
+
 struct module;
 struct key_entry;
 struct asus_wmi;
 
 struct asus_wmi_driver {
        bool                    hotplug_wireless;
+       int                     wapf;
 
        const char              *name;
        struct module           *owner;
@@ -44,6 +47,10 @@ struct asus_wmi_driver {
        const struct key_entry  *keymap;
        const char              *input_name;
        const char              *input_phys;
+       /* Returns new code, value, and autorelease values in arguments.
+        * Return ASUS_WMI_KEY_IGNORE in code if event should be ignored. */
+       void (*key_filter) (struct asus_wmi_driver *driver, int *code,
+                           unsigned int *value, bool *autorelease);
 
        int (*probe) (struct platform_device *device);
        void (*quirks) (struct asus_wmi_driver *driver);
index e39ab1d3ed878b6044a391db4be16b58224da0d2..f31fa4efa725504bc4a9e6b251686453d0cb7e6d 100644 (file)
@@ -612,7 +612,6 @@ static int __init dell_init(void)
        if (!bufferpage)
                goto fail_buffer;
        buffer = page_address(bufferpage);
-       mutex_init(&buffer_mutex);
 
        ret = dell_setup_rfkill();
 
index ce790827e199ab4d4d9784850023ac4321c7bcc2..fa9a2171cc134b733c837f89a4f1b80aeb0989ea 100644 (file)
@@ -54,6 +54,8 @@ MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
  */
 
 static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
+       { KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
+
        { KE_KEY, 0xe045, { KEY_PROG1 } },
        { KE_KEY, 0xe009, { KEY_EJECTCD } },
 
@@ -85,6 +87,11 @@ static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
        { KE_IGNORE, 0xe013, { KEY_RESERVED } },
 
        { KE_IGNORE, 0xe020, { KEY_MUTE } },
+
+       /* Shortcut and audio panel keys */
+       { KE_IGNORE, 0xe025, { KEY_RESERVED } },
+       { KE_IGNORE, 0xe026, { KEY_RESERVED } },
+
        { KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
        { KE_IGNORE, 0xe030, { KEY_VOLUMEUP } },
        { KE_IGNORE, 0xe033, { KEY_KBDILLUMUP } },
@@ -92,6 +99,9 @@ static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
        { KE_IGNORE, 0xe03a, { KEY_CAPSLOCK } },
        { KE_IGNORE, 0xe045, { KEY_NUMLOCK } },
        { KE_IGNORE, 0xe046, { KEY_SCROLLLOCK } },
+       { KE_IGNORE, 0xe0f7, { KEY_MUTE } },
+       { KE_IGNORE, 0xe0f8, { KEY_VOLUMEDOWN } },
+       { KE_IGNORE, 0xe0f9, { KEY_VOLUMEUP } },
        { KE_END, 0 }
 };
 
index 4aa867a9b88b8411f5231e9ac7a4298ffc9b8860..9f6e64302b457b04fb0871524e9ac906af34399f 100644 (file)
@@ -56,6 +56,11 @@ MODULE_PARM_DESC(hotplug_wireless,
                 "If your laptop needs that, please report to "
                 "acpi4asus-user@lists.sourceforge.net.");
 
+/* Values for T101MT "Home" key */
+#define HOME_PRESS     0xe4
+#define HOME_HOLD      0xea
+#define HOME_RELEASE   0xe5
+
 static const struct key_entry eeepc_wmi_keymap[] = {
        /* Sleep already handled via generic ACPI code */
        { KE_KEY, 0x30, { KEY_VOLUMEUP } },
@@ -71,6 +76,7 @@ static const struct key_entry eeepc_wmi_keymap[] = {
        { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
        { KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */
        { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */
+       { KE_KEY, HOME_PRESS, { KEY_CONFIG } }, /* Home/Express gate key */
        { KE_KEY, 0xe8, { KEY_SCREENLOCK } },
        { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } },
        { KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } },
@@ -81,6 +87,25 @@ static const struct key_entry eeepc_wmi_keymap[] = {
        { KE_END, 0},
 };
 
+static void eeepc_wmi_key_filter(struct asus_wmi_driver *asus_wmi, int *code,
+                                unsigned int *value, bool *autorelease)
+{
+       switch (*code) {
+       case HOME_PRESS:
+               *value = 1;
+               *autorelease = 0;
+               break;
+       case HOME_HOLD:
+               *code = ASUS_WMI_KEY_IGNORE;
+               break;
+       case HOME_RELEASE:
+               *code = HOME_PRESS;
+               *value = 0;
+               *autorelease = 0;
+               break;
+       }
+}
+
 static acpi_status eeepc_wmi_parse_device(acpi_handle handle, u32 level,
                                                 void *context, void **retval)
 {
@@ -141,6 +166,7 @@ static void eeepc_dmi_check(struct asus_wmi_driver *driver)
 static void eeepc_wmi_quirks(struct asus_wmi_driver *driver)
 {
        driver->hotplug_wireless = hotplug_wireless;
+       driver->wapf = -1;
        eeepc_dmi_check(driver);
 }
 
@@ -151,6 +177,7 @@ static struct asus_wmi_driver asus_wmi_driver = {
        .keymap = eeepc_wmi_keymap,
        .input_name = "Eee PC WMI hotkeys",
        .input_phys = EEEPC_WMI_FILE "/input0",
+       .key_filter = eeepc_wmi_key_filter,
        .probe = eeepc_wmi_probe,
        .quirks = eeepc_wmi_quirks,
 };
index bfdda33feb26547dea8a5187543bb4b58de38f01..0c595410e78897e8b6b3f4b9e9c84e765ac0ba57 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/input.h>
 #include <linux/input/sparse-keymap.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
 
 #define IDEAPAD_RFKILL_DEV_NUM (3)
 
+#define CFG_BT_BIT     (16)
+#define CFG_3G_BIT     (17)
+#define CFG_WIFI_BIT   (18)
+#define CFG_CAMERA_BIT (19)
+
 struct ideapad_private {
        struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
        struct platform_device *platform_device;
        struct input_dev *inputdev;
+       struct backlight_device *blightdev;
+       unsigned long cfg;
 };
 
 static acpi_handle ideapad_handle;
@@ -155,7 +164,7 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
 }
 
 /*
- * camera power
+ * sysfs
  */
 static ssize_t show_ideapad_cam(struct device *dev,
                                struct device_attribute *attr,
@@ -186,6 +195,44 @@ static ssize_t store_ideapad_cam(struct device *dev,
 
 static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
 
+static ssize_t show_ideapad_cfg(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct ideapad_private *priv = dev_get_drvdata(dev);
+
+       return sprintf(buf, "0x%.8lX\n", priv->cfg);
+}
+
+static DEVICE_ATTR(cfg, 0444, show_ideapad_cfg, NULL);
+
+static struct attribute *ideapad_attributes[] = {
+       &dev_attr_camera_power.attr,
+       &dev_attr_cfg.attr,
+       NULL
+};
+
+static mode_t ideapad_is_visible(struct kobject *kobj,
+                                struct attribute *attr,
+                                int idx)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct ideapad_private *priv = dev_get_drvdata(dev);
+       bool supported;
+
+       if (attr == &dev_attr_camera_power.attr)
+               supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
+       else
+               supported = true;
+
+       return supported ? attr->mode : 0;
+}
+
+static struct attribute_group ideapad_attribute_group = {
+       .is_visible = ideapad_is_visible,
+       .attrs = ideapad_attributes
+};
+
 /*
  * Rfkill
  */
@@ -197,9 +244,9 @@ struct ideapad_rfk_data {
 };
 
 const struct ideapad_rfk_data ideapad_rfk_data[] = {
-       { "ideapad_wlan",       18, 0x15, RFKILL_TYPE_WLAN },
-       { "ideapad_bluetooth",  16, 0x17, RFKILL_TYPE_BLUETOOTH },
-       { "ideapad_3g",         17, 0x20, RFKILL_TYPE_WWAN },
+       { "ideapad_wlan",      CFG_WIFI_BIT, 0x15, RFKILL_TYPE_WLAN },
+       { "ideapad_bluetooth", CFG_BT_BIT,   0x17, RFKILL_TYPE_BLUETOOTH },
+       { "ideapad_3g",        CFG_3G_BIT,   0x20, RFKILL_TYPE_WWAN },
 };
 
 static int ideapad_rfk_set(void *data, bool blocked)
@@ -265,8 +312,7 @@ static int __devinit ideapad_register_rfkill(struct acpi_device *adevice,
        return 0;
 }
 
-static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice,
-                                               int dev)
+static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
 {
        struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
 
@@ -280,15 +326,6 @@ static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice,
 /*
  * Platform device
  */
-static struct attribute *ideapad_attributes[] = {
-       &dev_attr_camera_power.attr,
-       NULL
-};
-
-static struct attribute_group ideapad_attribute_group = {
-       .attrs = ideapad_attributes
-};
-
 static int __devinit ideapad_platform_init(struct ideapad_private *priv)
 {
        int result;
@@ -369,7 +406,7 @@ err_free_dev:
        return error;
 }
 
-static void __devexit ideapad_input_exit(struct ideapad_private *priv)
+static void ideapad_input_exit(struct ideapad_private *priv)
 {
        sparse_keymap_free(priv->inputdev);
        input_unregister_device(priv->inputdev);
@@ -382,6 +419,98 @@ static void ideapad_input_report(struct ideapad_private *priv,
        sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
 }
 
+/*
+ * backlight
+ */
+static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
+{
+       unsigned long now;
+
+       if (read_ec_data(ideapad_handle, 0x12, &now))
+               return -EIO;
+       return now;
+}
+
+static int ideapad_backlight_update_status(struct backlight_device *blightdev)
+{
+       if (write_ec_cmd(ideapad_handle, 0x13, blightdev->props.brightness))
+               return -EIO;
+       if (write_ec_cmd(ideapad_handle, 0x33,
+                        blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
+               return -EIO;
+
+       return 0;
+}
+
+static const struct backlight_ops ideapad_backlight_ops = {
+       .get_brightness = ideapad_backlight_get_brightness,
+       .update_status = ideapad_backlight_update_status,
+};
+
+static int ideapad_backlight_init(struct ideapad_private *priv)
+{
+       struct backlight_device *blightdev;
+       struct backlight_properties props;
+       unsigned long max, now, power;
+
+       if (read_ec_data(ideapad_handle, 0x11, &max))
+               return -EIO;
+       if (read_ec_data(ideapad_handle, 0x12, &now))
+               return -EIO;
+       if (read_ec_data(ideapad_handle, 0x18, &power))
+               return -EIO;
+
+       memset(&props, 0, sizeof(struct backlight_properties));
+       props.max_brightness = max;
+       props.type = BACKLIGHT_PLATFORM;
+       blightdev = backlight_device_register("ideapad",
+                                             &priv->platform_device->dev,
+                                             priv,
+                                             &ideapad_backlight_ops,
+                                             &props);
+       if (IS_ERR(blightdev)) {
+               pr_err("Could not register backlight device\n");
+               return PTR_ERR(blightdev);
+       }
+
+       priv->blightdev = blightdev;
+       blightdev->props.brightness = now;
+       blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
+       backlight_update_status(blightdev);
+
+       return 0;
+}
+
+static void ideapad_backlight_exit(struct ideapad_private *priv)
+{
+       if (priv->blightdev)
+               backlight_device_unregister(priv->blightdev);
+       priv->blightdev = NULL;
+}
+
+static void ideapad_backlight_notify_power(struct ideapad_private *priv)
+{
+       unsigned long power;
+       struct backlight_device *blightdev = priv->blightdev;
+
+       if (read_ec_data(ideapad_handle, 0x18, &power))
+               return;
+       blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
+}
+
+static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
+{
+       unsigned long now;
+
+       /* if we control brightness via acpi video driver */
+       if (priv->blightdev == NULL) {
+               read_ec_data(ideapad_handle, 0x12, &now);
+               return;
+       }
+
+       backlight_force_update(priv->blightdev, BACKLIGHT_UPDATE_HOTKEY);
+}
+
 /*
  * module init/exit
  */
@@ -393,10 +522,11 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
 
 static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
 {
-       int ret, i, cfg;
+       int ret, i;
+       unsigned long cfg;
        struct ideapad_private *priv;
 
-       if (read_method_int(adevice->handle, "_CFG", &cfg))
+       if (read_method_int(adevice->handle, "_CFG", (int *)&cfg))
                return -ENODEV;
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -404,6 +534,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
                return -ENOMEM;
        dev_set_drvdata(&adevice->dev, priv);
        ideapad_handle = adevice->handle;
+       priv->cfg = cfg;
 
        ret = ideapad_platform_init(priv);
        if (ret)
@@ -414,15 +545,25 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
                goto input_failed;
 
        for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) {
-               if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg))
+               if (test_bit(ideapad_rfk_data[i].cfgbit, &cfg))
                        ideapad_register_rfkill(adevice, i);
                else
                        priv->rfk[i] = NULL;
        }
        ideapad_sync_rfk_state(adevice);
 
+       if (!acpi_video_backlight_support()) {
+               ret = ideapad_backlight_init(priv);
+               if (ret && ret != -ENODEV)
+                       goto backlight_failed;
+       }
+
        return 0;
 
+backlight_failed:
+       for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
+               ideapad_unregister_rfkill(adevice, i);
+       ideapad_input_exit(priv);
 input_failed:
        ideapad_platform_exit(priv);
 platform_failed:
@@ -435,6 +576,7 @@ static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type)
        struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
        int i;
 
+       ideapad_backlight_exit(priv);
        for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
                ideapad_unregister_rfkill(adevice, i);
        ideapad_input_exit(priv);
@@ -459,12 +601,19 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
        vpc1 = (vpc2 << 8) | vpc1;
        for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
                if (test_bit(vpc_bit, &vpc1)) {
-                       if (vpc_bit == 9)
+                       switch (vpc_bit) {
+                       case 9:
                                ideapad_sync_rfk_state(adevice);
-                       else if (vpc_bit == 4)
-                               read_ec_data(handle, 0x12, &vpc2);
-                       else
+                               break;
+                       case 4:
+                               ideapad_backlight_notify_brightness(priv);
+                               break;
+                       case 2:
+                               ideapad_backlight_notify_power(priv);
+                               break;
+                       default:
                                ideapad_input_report(priv, vpc_bit);
+                       }
                }
        }
 }
index 5ffe7c3981482f7076ea95172cd0e960a04e465f..809a3ae943c6997f65a20328f8fe2fb02690cd6c 100644 (file)
@@ -403,7 +403,7 @@ static void ips_cpu_raise(struct ips_driver *ips)
 
        thm_writew(THM_MPCPC, (new_tdp_limit * 10) / 8);
 
-       turbo_override |= TURBO_TDC_OVR_EN | TURBO_TDC_OVR_EN;
+       turbo_override |= TURBO_TDC_OVR_EN | TURBO_TDP_OVR_EN;
        wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override);
 
        turbo_override &= ~TURBO_TDP_MASK;
@@ -438,7 +438,7 @@ static void ips_cpu_lower(struct ips_driver *ips)
 
        thm_writew(THM_MPCPC, (new_limit * 10) / 8);
 
-       turbo_override |= TURBO_TDC_OVR_EN | TURBO_TDC_OVR_EN;
+       turbo_override |= TURBO_TDC_OVR_EN | TURBO_TDP_OVR_EN;
        wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override);
 
        turbo_override &= ~TURBO_TDP_MASK;
index 809adea4965f96a8fca6f703fd94b40523d33072..abddc83e9fd7eb55d3e0ad3c0a4d8a5ad01070f0 100644 (file)
@@ -477,6 +477,8 @@ static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl,
                return AE_ERROR;
        }
 
+       return AE_OK;
+
  aux1_not_found:
        if (status == AE_NOT_FOUND)
                return AE_OK;
index 3a578323122b7b18750de7d31ecb570b878cef43..ccd7b1f8351929992891f8cf3c31f3e30e9b3998 100644 (file)
@@ -493,20 +493,30 @@ static int mid_thermal_probe(struct platform_device *pdev)
 
        /* Register each sensor with the generic thermal framework*/
        for (i = 0; i < MSIC_THERMAL_SENSORS; i++) {
+               struct thermal_device_info *td_info = initialize_sensor(i);
+
+               if (!td_info) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
                pinfo->tzd[i] = thermal_zone_device_register(name[i],
-                               0, initialize_sensor(i), &tzd_ops, 0, 0, 0, 0);
-               if (IS_ERR(pinfo->tzd[i]))
-                       goto reg_fail;
+                               0, td_info, &tzd_ops, 0, 0, 0, 0);
+               if (IS_ERR(pinfo->tzd[i])) {
+                       kfree(td_info);
+                       ret = PTR_ERR(pinfo->tzd[i]);
+                       goto err;
+               }
        }
 
        pinfo->pdev = pdev;
        platform_set_drvdata(pdev, pinfo);
        return 0;
 
-reg_fail:
-       ret = PTR_ERR(pinfo->tzd[i]);
-       while (--i >= 0)
+err:
+       while (--i >= 0) {
+               kfree(pinfo->tzd[i]->devdata);
                thermal_zone_device_unregister(pinfo->tzd[i]);
+       }
        configure_adc(0);
        kfree(pinfo);
        return ret;
@@ -524,8 +534,10 @@ static int mid_thermal_remove(struct platform_device *pdev)
        int i;
        struct platform_info *pinfo = platform_get_drvdata(pdev);
 
-       for (i = 0; i < MSIC_THERMAL_SENSORS; i++)
+       for (i = 0; i < MSIC_THERMAL_SENSORS; i++) {
+               kfree(pinfo->tzd[i]->devdata);
                thermal_zone_device_unregister(pinfo->tzd[i]);
+       }
 
        kfree(pinfo);
        platform_set_drvdata(pdev, NULL);
index bde47e9080cd9a131dc76563e42d01e273bab990..c8a6aed452778d489a53bb9a60c2a9af2ec72470 100644 (file)
@@ -637,15 +637,13 @@ end_function:
        return error;
 }
 
-const struct pci_device_id rar_pci_id_tbl[] = {
+static DEFINE_PCI_DEVICE_TABLE(rar_pci_id_tbl) = {
        { PCI_VDEVICE(INTEL, 0x4110) },
        { 0 }
 };
 
 MODULE_DEVICE_TABLE(pci, rar_pci_id_tbl);
 
-const struct pci_device_id *my_id_table = rar_pci_id_tbl;
-
 /* field for registering driver to PCI device */
 static struct pci_driver rar_pci_driver = {
        .name = "rar_register_driver",
index 940accbe28d3a2db53c745217bf85f2dd36a1bc0..c86665369a22d4b24a23707d5b7f24d955dc2a1a 100644 (file)
@@ -725,7 +725,7 @@ static void ipc_remove(struct pci_dev *pdev)
        intel_scu_devices_destroy();
 }
 
-static const struct pci_device_id pci_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(pci_ids) = {
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080e)},
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)},
        { 0,}
index 3ff629df9f01b0b1a0a6ff2be71115d922c71b88..f204643c5052640908063b07e0288263ca8f8548 100644 (file)
@@ -538,6 +538,15 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
                },
                .callback = dmi_check_cb
        },
+       {
+               .ident = "MSI U270",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR,
+                               "Micro-Star International Co., Ltd."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"),
+               },
+               .callback = dmi_check_cb
+       },
        { }
 };
 
@@ -996,3 +1005,4 @@ MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N034:*");
 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");
 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");
 MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");
+MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*");
index c832e3356cd61f03036ad8802919285add489466..6f40bf202dc7cf9e6baa80a329a3ea1810fcb5ed 100644 (file)
@@ -272,6 +272,7 @@ static int __init msi_wmi_init(void)
 err_free_backlight:
        backlight_device_unregister(backlight);
 err_free_input:
+       sparse_keymap_free(msi_wmi_input_dev);
        input_unregister_device(msi_wmi_input_dev);
 err_uninstall_notifier:
        wmi_remove_notify_handler(MSIWMI_EVENT_GUID);
index d347116d150e38146eedf6e817e51afc84898169..359163011044d4fced62b95164a7d8baee399cc1 100644 (file)
@@ -520,6 +520,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
                },
                .callback = dmi_check_cb,
        },
+       {
+               .ident = "N510",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR,
+                                       "SAMSUNG ELECTRONICS CO., LTD."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "N510"),
+                       DMI_MATCH(DMI_BOARD_NAME, "N510"),
+               },
+               .callback = dmi_check_cb,
+       },
        {
                .ident = "X125",
                .matches = {
@@ -600,6 +610,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
                },
                .callback = dmi_check_cb,
        },
+       {
+               .ident = "N150/N210/N220",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR,
+                                       "SAMSUNG ELECTRONICS CO., LTD."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
+                       DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
+               },
+               .callback = dmi_check_cb,
+       },
        {
                .ident = "N150/N210/N220/N230",
                .matches = {
diff --git a/drivers/platform/x86/samsung-q10.c b/drivers/platform/x86/samsung-q10.c
new file mode 100644 (file)
index 0000000..1e54ae7
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ *  Driver for Samsung Q10 and related laptops: controls the backlight
+ *
+ *  Copyright (c) 2011 Frederick van der Wyck <fvanderwyck@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
+ *  published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/backlight.h>
+#include <linux/i8042.h>
+#include <linux/dmi.h>
+
+#define SAMSUNGQ10_BL_MAX_INTENSITY      255
+#define SAMSUNGQ10_BL_DEFAULT_INTENSITY  185
+
+#define SAMSUNGQ10_BL_8042_CMD           0xbe
+#define SAMSUNGQ10_BL_8042_DATA          { 0x89, 0x91 }
+
+static int samsungq10_bl_brightness;
+
+static bool force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force,
+               "Disable the DMI check and force the driver to be loaded");
+
+static int samsungq10_bl_set_intensity(struct backlight_device *bd)
+{
+
+       int brightness = bd->props.brightness;
+       unsigned char c[3] = SAMSUNGQ10_BL_8042_DATA;
+
+       c[2] = (unsigned char)brightness;
+       i8042_lock_chip();
+       i8042_command(c, (0x30 << 8) | SAMSUNGQ10_BL_8042_CMD);
+       i8042_unlock_chip();
+       samsungq10_bl_brightness = brightness;
+
+       return 0;
+}
+
+static int samsungq10_bl_get_intensity(struct backlight_device *bd)
+{
+       return samsungq10_bl_brightness;
+}
+
+static const struct backlight_ops samsungq10_bl_ops = {
+       .get_brightness = samsungq10_bl_get_intensity,
+       .update_status  = samsungq10_bl_set_intensity,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int samsungq10_suspend(struct device *dev)
+{
+       return 0;
+}
+
+static int samsungq10_resume(struct device *dev)
+{
+
+       struct backlight_device *bd = dev_get_drvdata(dev);
+
+       samsungq10_bl_set_intensity(bd);
+       return 0;
+}
+#else
+#define samsungq10_suspend NULL
+#define samsungq10_resume  NULL
+#endif
+
+static SIMPLE_DEV_PM_OPS(samsungq10_pm_ops,
+                         samsungq10_suspend, samsungq10_resume);
+
+static int __devinit samsungq10_probe(struct platform_device *pdev)
+{
+
+       struct backlight_properties props;
+       struct backlight_device *bd;
+
+       memset(&props, 0, sizeof(struct backlight_properties));
+       props.type = BACKLIGHT_PLATFORM;
+       props.max_brightness = SAMSUNGQ10_BL_MAX_INTENSITY;
+       bd = backlight_device_register("samsung", &pdev->dev, NULL,
+                                      &samsungq10_bl_ops, &props);
+       if (IS_ERR(bd))
+               return PTR_ERR(bd);
+
+       platform_set_drvdata(pdev, bd);
+
+       bd->props.brightness = SAMSUNGQ10_BL_DEFAULT_INTENSITY;
+       samsungq10_bl_set_intensity(bd);
+
+       return 0;
+}
+
+static int __devexit samsungq10_remove(struct platform_device *pdev)
+{
+
+       struct backlight_device *bd = platform_get_drvdata(pdev);
+
+       bd->props.brightness = SAMSUNGQ10_BL_DEFAULT_INTENSITY;
+       samsungq10_bl_set_intensity(bd);
+
+       backlight_device_unregister(bd);
+
+       return 0;
+}
+
+static struct platform_driver samsungq10_driver = {
+       .driver         = {
+               .name   = KBUILD_MODNAME,
+               .owner  = THIS_MODULE,
+               .pm     = &samsungq10_pm_ops,
+       },
+       .probe          = samsungq10_probe,
+       .remove         = __devexit_p(samsungq10_remove),
+};
+
+static struct platform_device *samsungq10_device;
+
+static int __init dmi_check_callback(const struct dmi_system_id *id)
+{
+       printk(KERN_INFO KBUILD_MODNAME ": found model '%s'\n", id->ident);
+       return 1;
+}
+
+static struct dmi_system_id __initdata samsungq10_dmi_table[] = {
+       {
+               .ident = "Samsung Q10",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Samsung"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "SQ10"),
+               },
+               .callback = dmi_check_callback,
+       },
+       {
+               .ident = "Samsung Q20",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG Electronics"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "SENS Q20"),
+               },
+               .callback = dmi_check_callback,
+       },
+       {
+               .ident = "Samsung Q25",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG Electronics"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "NQ25"),
+               },
+               .callback = dmi_check_callback,
+       },
+       {
+               .ident = "Dell Latitude X200",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X200"),
+               },
+               .callback = dmi_check_callback,
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(dmi, samsungq10_dmi_table);
+
+static int __init samsungq10_init(void)
+{
+       if (!force && !dmi_check_system(samsungq10_dmi_table))
+               return -ENODEV;
+
+       samsungq10_device = platform_create_bundle(&samsungq10_driver,
+                                                  samsungq10_probe,
+                                                  NULL, 0, NULL, 0);
+
+       if (IS_ERR(samsungq10_device))
+               return PTR_ERR(samsungq10_device);
+
+       return 0;
+}
+
+static void __exit samsungq10_exit(void)
+{
+       platform_device_unregister(samsungq10_device);
+       platform_driver_unregister(&samsungq10_driver);
+}
+
+module_init(samsungq10_init);
+module_exit(samsungq10_exit);
+
+MODULE_AUTHOR("Frederick van der Wyck <fvanderwyck@gmail.com>");
+MODULE_DESCRIPTION("Samsung Q10 Driver");
+MODULE_LICENSE("GPL");
index 26c5b117df22d5011c46c64f2f588f83dd7ed58c..7bd829f247eb5ae98fa3b2c61914d16cf554a941 100644 (file)
@@ -3185,9 +3185,18 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
 
                KEY_VENDOR,     /* 0x17: Thinkpad/AccessIBM/Lenovo */
 
+               /* (assignments unknown, please report if found) */
+               KEY_UNKNOWN, KEY_UNKNOWN,
+
+               /*
+                * The mic mute button only sends 0x1a.  It does not
+                * automatically mute the mic or change the mute light.
+                */
+               KEY_MICMUTE,    /* 0x1a: Mic mute (since ?400 or so) */
+
                /* (assignments unknown, please report if found) */
                KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
-               KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+               KEY_UNKNOWN,
                },
        };
 
index d3e38790906ed889de2b78454516ec1410eb6466..d8e6a429e8ba046027e9fa8550be6444f47ae971 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/slab.h>
+#include <linux/async.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/suspend.h>
@@ -33,6 +34,8 @@
 
 #include "dummy.h"
 
+#define rdev_crit(rdev, fmt, ...)                                      \
+       pr_crit("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
 #define rdev_err(rdev, fmt, ...)                                       \
        pr_err("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
 #define rdev_warn(rdev, fmt, ...)                                      \
@@ -78,11 +81,13 @@ struct regulator {
        char *supply_name;
        struct device_attribute dev_attr;
        struct regulator_dev *rdev;
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs;
+#endif
 };
 
 static int _regulator_is_enabled(struct regulator_dev *rdev);
-static int _regulator_disable(struct regulator_dev *rdev,
-               struct regulator_dev **supply_rdev_ptr);
+static int _regulator_disable(struct regulator_dev *rdev);
 static int _regulator_get_voltage(struct regulator_dev *rdev);
 static int _regulator_get_current_limit(struct regulator_dev *rdev);
 static unsigned int _regulator_get_mode(struct regulator_dev *rdev);
@@ -90,6 +95,9 @@ static void _notifier_call_chain(struct regulator_dev *rdev,
                                  unsigned long event, void *data);
 static int _regulator_do_set_voltage(struct regulator_dev *rdev,
                                     int min_uV, int max_uV);
+static struct regulator *create_regulator(struct regulator_dev *rdev,
+                                         struct device *dev,
+                                         const char *supply_name);
 
 static const char *rdev_get_name(struct regulator_dev *rdev)
 {
@@ -143,8 +151,11 @@ static int regulator_check_voltage(struct regulator_dev *rdev,
        if (*min_uV < rdev->constraints->min_uV)
                *min_uV = rdev->constraints->min_uV;
 
-       if (*min_uV > *max_uV)
+       if (*min_uV > *max_uV) {
+               rdev_err(rdev, "unsupportable voltage range: %d-%duV\n",
+                        *min_uV, *max_uV);
                return -EINVAL;
+       }
 
        return 0;
 }
@@ -197,8 +208,11 @@ static int regulator_check_current_limit(struct regulator_dev *rdev,
        if (*min_uA < rdev->constraints->min_uA)
                *min_uA = rdev->constraints->min_uA;
 
-       if (*min_uA > *max_uA)
+       if (*min_uA > *max_uA) {
+               rdev_err(rdev, "unsupportable current range: %d-%duA\n",
+                        *min_uA, *max_uA);
                return -EINVAL;
+       }
 
        return 0;
 }
@@ -213,6 +227,7 @@ static int regulator_mode_constrain(struct regulator_dev *rdev, int *mode)
        case REGULATOR_MODE_STANDBY:
                break;
        default:
+               rdev_err(rdev, "invalid mode %x specified\n", *mode);
                return -EINVAL;
        }
 
@@ -779,7 +794,6 @@ static int machine_constraints_voltage(struct regulator_dev *rdev,
                if (ret < 0) {
                        rdev_err(rdev, "failed to apply %duV constraint\n",
                                 rdev->constraints->min_uV);
-                       rdev->constraints = NULL;
                        return ret;
                }
        }
@@ -882,7 +896,6 @@ static int set_machine_constraints(struct regulator_dev *rdev,
                ret = suspend_prepare(rdev, rdev->constraints->initial_state);
                if (ret < 0) {
                        rdev_err(rdev, "failed to set suspend state\n");
-                       rdev->constraints = NULL;
                        goto out;
                }
        }
@@ -909,13 +922,15 @@ static int set_machine_constraints(struct regulator_dev *rdev,
                ret = ops->enable(rdev);
                if (ret < 0) {
                        rdev_err(rdev, "failed to enable\n");
-                       rdev->constraints = NULL;
                        goto out;
                }
        }
 
        print_constraints(rdev);
+       return 0;
 out:
+       kfree(rdev->constraints);
+       rdev->constraints = NULL;
        return ret;
 }
 
@@ -929,21 +944,20 @@ out:
  * core if it's child is enabled.
  */
 static int set_supply(struct regulator_dev *rdev,
-       struct regulator_dev *supply_rdev)
+                     struct regulator_dev *supply_rdev)
 {
        int err;
 
-       err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj,
-                               "supply");
-       if (err) {
-               rdev_err(rdev, "could not add device link %s err %d\n",
-                        supply_rdev->dev.kobj.name, err);
-                      goto out;
+       rdev_info(rdev, "supplied by %s\n", rdev_get_name(supply_rdev));
+
+       rdev->supply = create_regulator(supply_rdev, &rdev->dev, "SUPPLY");
+       if (IS_ERR(rdev->supply)) {
+               err = PTR_ERR(rdev->supply);
+               rdev->supply = NULL;
+               return err;
        }
-       rdev->supply = supply_rdev;
-       list_add(&rdev->slist, &supply_rdev->supply_list);
-out:
-       return err;
+
+       return 0;
 }
 
 /**
@@ -1032,7 +1046,7 @@ static void unset_regulator_supplies(struct regulator_dev *rdev)
        }
 }
 
-#define REG_STR_SIZE   32
+#define REG_STR_SIZE   64
 
 static struct regulator *create_regulator(struct regulator_dev *rdev,
                                          struct device *dev,
@@ -1052,8 +1066,9 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
 
        if (dev) {
                /* create a 'requested_microamps_name' sysfs entry */
-               size = scnprintf(buf, REG_STR_SIZE, "microamps_requested_%s",
-                       supply_name);
+               size = scnprintf(buf, REG_STR_SIZE,
+                                "microamps_requested_%s-%s",
+                                dev_name(dev), supply_name);
                if (size >= REG_STR_SIZE)
                        goto overflow_err;
 
@@ -1088,7 +1103,28 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
                                  dev->kobj.name, err);
                        goto link_name_err;
                }
+       } else {
+               regulator->supply_name = kstrdup(supply_name, GFP_KERNEL);
+               if (regulator->supply_name == NULL)
+                       goto attr_err;
+       }
+
+#ifdef CONFIG_DEBUG_FS
+       regulator->debugfs = debugfs_create_dir(regulator->supply_name,
+                                               rdev->debugfs);
+       if (IS_ERR_OR_NULL(regulator->debugfs)) {
+               rdev_warn(rdev, "Failed to create debugfs directory\n");
+               regulator->debugfs = NULL;
+       } else {
+               debugfs_create_u32("uA_load", 0444, regulator->debugfs,
+                                  &regulator->uA_load);
+               debugfs_create_u32("min_uV", 0444, regulator->debugfs,
+                                  &regulator->min_uV);
+               debugfs_create_u32("max_uV", 0444, regulator->debugfs,
+                                  &regulator->max_uV);
        }
+#endif
+
        mutex_unlock(&rdev->mutex);
        return regulator;
 link_name_err:
@@ -1267,13 +1303,17 @@ void regulator_put(struct regulator *regulator)
        mutex_lock(&regulator_list_mutex);
        rdev = regulator->rdev;
 
+#ifdef CONFIG_DEBUG_FS
+       debugfs_remove_recursive(regulator->debugfs);
+#endif
+
        /* remove any sysfs entries */
        if (regulator->dev) {
                sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name);
-               kfree(regulator->supply_name);
                device_remove_file(regulator->dev, &regulator->dev_attr);
                kfree(regulator->dev_attr.attr.name);
        }
+       kfree(regulator->supply_name);
        list_del(&regulator->list);
        kfree(regulator);
 
@@ -1301,19 +1341,6 @@ static int _regulator_enable(struct regulator_dev *rdev)
 {
        int ret, delay;
 
-       if (rdev->use_count == 0) {
-               /* do we need to enable the supply regulator first */
-               if (rdev->supply) {
-                       mutex_lock(&rdev->supply->mutex);
-                       ret = _regulator_enable(rdev->supply);
-                       mutex_unlock(&rdev->supply->mutex);
-                       if (ret < 0) {
-                               rdev_err(rdev, "failed to enable: %d\n", ret);
-                               return ret;
-                       }
-               }
-       }
-
        /* check voltage and requested load before enabling */
        if (rdev->constraints &&
            (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS))
@@ -1388,19 +1415,27 @@ int regulator_enable(struct regulator *regulator)
        struct regulator_dev *rdev = regulator->rdev;
        int ret = 0;
 
+       if (rdev->supply) {
+               ret = regulator_enable(rdev->supply);
+               if (ret != 0)
+                       return ret;
+       }
+
        mutex_lock(&rdev->mutex);
        ret = _regulator_enable(rdev);
        mutex_unlock(&rdev->mutex);
+
+       if (ret != 0)
+               regulator_disable(rdev->supply);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(regulator_enable);
 
 /* locks held by regulator_disable() */
-static int _regulator_disable(struct regulator_dev *rdev,
-               struct regulator_dev **supply_rdev_ptr)
+static int _regulator_disable(struct regulator_dev *rdev)
 {
        int ret = 0;
-       *supply_rdev_ptr = NULL;
 
        if (WARN(rdev->use_count <= 0,
                 "unbalanced disables for %s\n", rdev_get_name(rdev)))
@@ -1427,9 +1462,6 @@ static int _regulator_disable(struct regulator_dev *rdev,
                                             NULL);
                }
 
-               /* decrease our supplies ref count and disable if required */
-               *supply_rdev_ptr = rdev->supply;
-
                rdev->use_count = 0;
        } else if (rdev->use_count > 1) {
 
@@ -1440,6 +1472,7 @@ static int _regulator_disable(struct regulator_dev *rdev,
 
                rdev->use_count--;
        }
+
        return ret;
 }
 
@@ -1458,29 +1491,21 @@ static int _regulator_disable(struct regulator_dev *rdev,
 int regulator_disable(struct regulator *regulator)
 {
        struct regulator_dev *rdev = regulator->rdev;
-       struct regulator_dev *supply_rdev = NULL;
        int ret = 0;
 
        mutex_lock(&rdev->mutex);
-       ret = _regulator_disable(rdev, &supply_rdev);
+       ret = _regulator_disable(rdev);
        mutex_unlock(&rdev->mutex);
 
-       /* decrease our supplies ref count and disable if required */
-       while (supply_rdev != NULL) {
-               rdev = supply_rdev;
-
-               mutex_lock(&rdev->mutex);
-               _regulator_disable(rdev, &supply_rdev);
-               mutex_unlock(&rdev->mutex);
-       }
+       if (ret == 0 && rdev->supply)
+               regulator_disable(rdev->supply);
 
        return ret;
 }
 EXPORT_SYMBOL_GPL(regulator_disable);
 
 /* locks held by regulator_force_disable() */
-static int _regulator_force_disable(struct regulator_dev *rdev,
-               struct regulator_dev **supply_rdev_ptr)
+static int _regulator_force_disable(struct regulator_dev *rdev)
 {
        int ret = 0;
 
@@ -1497,10 +1522,6 @@ static int _regulator_force_disable(struct regulator_dev *rdev,
                        REGULATOR_EVENT_DISABLE, NULL);
        }
 
-       /* decrease our supplies ref count and disable if required */
-       *supply_rdev_ptr = rdev->supply;
-
-       rdev->use_count = 0;
        return ret;
 }
 
@@ -1516,16 +1537,16 @@ static int _regulator_force_disable(struct regulator_dev *rdev,
 int regulator_force_disable(struct regulator *regulator)
 {
        struct regulator_dev *rdev = regulator->rdev;
-       struct regulator_dev *supply_rdev = NULL;
        int ret;
 
        mutex_lock(&rdev->mutex);
        regulator->uA_load = 0;
-       ret = _regulator_force_disable(rdev, &supply_rdev);
+       ret = _regulator_force_disable(regulator->rdev);
        mutex_unlock(&rdev->mutex);
 
-       if (supply_rdev)
-               regulator_disable(get_device_regulator(rdev_get_dev(supply_rdev)));
+       if (rdev->supply)
+               while (rdev->open_count--)
+                       regulator_disable(rdev->supply);
 
        return ret;
 }
@@ -2136,7 +2157,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
        /* get input voltage */
        input_uV = 0;
        if (rdev->supply)
-               input_uV = _regulator_get_voltage(rdev->supply);
+               input_uV = regulator_get_voltage(rdev->supply);
        if (input_uV <= 0)
                input_uV = rdev->constraints->input_uV;
        if (input_uV <= 0) {
@@ -2206,17 +2227,8 @@ EXPORT_SYMBOL_GPL(regulator_unregister_notifier);
 static void _notifier_call_chain(struct regulator_dev *rdev,
                                  unsigned long event, void *data)
 {
-       struct regulator_dev *_rdev;
-
        /* call rdev chain first */
        blocking_notifier_call_chain(&rdev->notifier, event, NULL);
-
-       /* now notify regulator we supply */
-       list_for_each_entry(_rdev, &rdev->supply_list, slist) {
-               mutex_lock(&_rdev->mutex);
-               _notifier_call_chain(_rdev, event, data);
-               mutex_unlock(&_rdev->mutex);
-       }
 }
 
 /**
@@ -2264,6 +2276,13 @@ err:
 }
 EXPORT_SYMBOL_GPL(regulator_bulk_get);
 
+static void regulator_bulk_enable_async(void *data, async_cookie_t cookie)
+{
+       struct regulator_bulk_data *bulk = data;
+
+       bulk->ret = regulator_enable(bulk->consumer);
+}
+
 /**
  * regulator_bulk_enable - enable multiple regulator consumers
  *
@@ -2279,21 +2298,33 @@ EXPORT_SYMBOL_GPL(regulator_bulk_get);
 int regulator_bulk_enable(int num_consumers,
                          struct regulator_bulk_data *consumers)
 {
+       LIST_HEAD(async_domain);
        int i;
-       int ret;
+       int ret = 0;
+
+       for (i = 0; i < num_consumers; i++)
+               async_schedule_domain(regulator_bulk_enable_async,
+                                     &consumers[i], &async_domain);
+
+       async_synchronize_full_domain(&async_domain);
 
+       /* If any consumer failed we need to unwind any that succeeded */
        for (i = 0; i < num_consumers; i++) {
-               ret = regulator_enable(consumers[i].consumer);
-               if (ret != 0)
+               if (consumers[i].ret != 0) {
+                       ret = consumers[i].ret;
                        goto err;
+               }
        }
 
        return 0;
 
 err:
-       pr_err("Failed to enable %s: %d\n", consumers[i].supply, ret);
-       for (--i; i >= 0; --i)
-               regulator_disable(consumers[i].consumer);
+       for (i = 0; i < num_consumers; i++)
+               if (consumers[i].ret == 0)
+                       regulator_disable(consumers[i].consumer);
+               else
+                       pr_err("Failed to enable %s: %d\n",
+                              consumers[i].supply, consumers[i].ret);
 
        return ret;
 }
@@ -2589,9 +2620,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
        rdev->owner = regulator_desc->owner;
        rdev->desc = regulator_desc;
        INIT_LIST_HEAD(&rdev->consumer_list);
-       INIT_LIST_HEAD(&rdev->supply_list);
        INIT_LIST_HEAD(&rdev->list);
-       INIT_LIST_HEAD(&rdev->slist);
        BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
 
        /* preform any regulator specific init */
@@ -2672,6 +2701,7 @@ unset_supplies:
        unset_regulator_supplies(rdev);
 
 scrub:
+       kfree(rdev->constraints);
        device_unregister(&rdev->dev);
        /* device core frees rdev */
        rdev = ERR_PTR(ret);
@@ -2703,7 +2733,7 @@ void regulator_unregister(struct regulator_dev *rdev)
        unset_regulator_supplies(rdev);
        list_del(&rdev->list);
        if (rdev->supply)
-               sysfs_remove_link(&rdev->dev.kobj, "supply");
+               regulator_put(rdev->supply);
        device_unregister(&rdev->dev);
        kfree(rdev->constraints);
        mutex_unlock(&regulator_list_mutex);
index c7410bde7b5d92ec128bbb79850c62e0e9495d87..f6ef6694ab9896687074c9224e4e60fea919acbc 100644 (file)
@@ -36,6 +36,29 @@ static struct regulator_desc dummy_desc = {
        .ops = &dummy_ops,
 };
 
+static int __devinit dummy_regulator_probe(struct platform_device *pdev)
+{
+       int ret;
+
+       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);
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct platform_driver dummy_regulator_driver = {
+       .probe          = dummy_regulator_probe,
+       .driver         = {
+               .name           = "reg-dummy",
+               .owner          = THIS_MODULE,
+       },
+};
+
 static struct platform_device *dummy_pdev;
 
 void __init regulator_dummy_init(void)
@@ -55,12 +78,9 @@ void __init regulator_dummy_init(void)
                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);
+       ret = platform_driver_register(&dummy_regulator_driver);
+       if (ret != 0) {
+               pr_err("Failed to register dummy regulator driver: %d\n", ret);
                platform_device_unregister(dummy_pdev);
-               return;
        }
 }
index 55dd4e6650db80caa02d42a61f7315b90473b809..66d2d60b436a3e7a28092d64a499fd203db764a5 100644 (file)
@@ -49,7 +49,6 @@
 #define TPS65911_REG_LDO7              11
 #define TPS65911_REG_LDO8              12
 
-#define TPS65910_NUM_REGULATOR         13
 #define TPS65910_SUPPLY_STATE_ENABLED  0x1
 
 /* supported VIO voltages in milivolts */
@@ -264,11 +263,12 @@ static struct tps_info tps65911_regs[] = {
 };
 
 struct tps65910_reg {
-       struct regulator_desc desc[TPS65910_NUM_REGULATOR];
+       struct regulator_desc *desc;
        struct tps65910 *mfd;
-       struct regulator_dev *rdev[TPS65910_NUM_REGULATOR];
-       struct tps_info *info[TPS65910_NUM_REGULATOR];
+       struct regulator_dev **rdev;
+       struct tps_info **info;
        struct mutex mutex;
+       int num_regulators;
        int mode;
        int  (*get_ctrl_reg)(int);
 };
@@ -759,8 +759,13 @@ static int tps65910_list_voltage_dcdc(struct regulator_dev *dev,
                mult = (selector / VDD1_2_NUM_VOLTS) + 1;
                volt = VDD1_2_MIN_VOLT +
                                (selector % VDD1_2_NUM_VOLTS) * VDD1_2_OFFSET;
+               break;
        case TPS65911_REG_VDDCTRL:
                volt = VDDCTRL_MIN_VOLT + (selector * VDDCTRL_OFFSET);
+               break;
+       default:
+               BUG();
+               return -EINVAL;
        }
 
        return  volt * 100 * mult;
@@ -897,16 +902,42 @@ static __devinit int tps65910_probe(struct platform_device *pdev)
        switch(tps65910_chip_id(tps65910)) {
        case TPS65910:
                pmic->get_ctrl_reg = &tps65910_get_ctrl_register;
+               pmic->num_regulators = ARRAY_SIZE(tps65910_regs);
                info = tps65910_regs;
+               break;
        case TPS65911:
                pmic->get_ctrl_reg = &tps65911_get_ctrl_register;
+               pmic->num_regulators = ARRAY_SIZE(tps65911_regs);
                info = tps65911_regs;
+               break;
        default:
                pr_err("Invalid tps chip version\n");
+               kfree(pmic);
                return -ENODEV;
        }
 
-       for (i = 0; i < TPS65910_NUM_REGULATOR; i++, info++, reg_data++) {
+       pmic->desc = kcalloc(pmic->num_regulators,
+                       sizeof(struct regulator_desc), GFP_KERNEL);
+       if (!pmic->desc) {
+               err = -ENOMEM;
+               goto err_free_pmic;
+       }
+
+       pmic->info = kcalloc(pmic->num_regulators,
+                       sizeof(struct tps_info *), GFP_KERNEL);
+       if (!pmic->info) {
+               err = -ENOMEM;
+               goto err_free_desc;
+       }
+
+       pmic->rdev = kcalloc(pmic->num_regulators,
+                       sizeof(struct regulator_dev *), GFP_KERNEL);
+       if (!pmic->rdev) {
+               err = -ENOMEM;
+               goto err_free_info;
+       }
+
+       for (i = 0; i < pmic->num_regulators; i++, info++, reg_data++) {
                /* Register the regulators */
                pmic->info[i] = info;
 
@@ -938,7 +969,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev)
                                "failed to register %s regulator\n",
                                pdev->name);
                        err = PTR_ERR(rdev);
-                       goto err;
+                       goto err_unregister_regulator;
                }
 
                /* Save regulator for cleanup */
@@ -946,23 +977,31 @@ static __devinit int tps65910_probe(struct platform_device *pdev)
        }
        return 0;
 
-err:
+err_unregister_regulator:
        while (--i >= 0)
                regulator_unregister(pmic->rdev[i]);
-
+       kfree(pmic->rdev);
+err_free_info:
+       kfree(pmic->info);
+err_free_desc:
+       kfree(pmic->desc);
+err_free_pmic:
        kfree(pmic);
        return err;
 }
 
 static int __devexit tps65910_remove(struct platform_device *pdev)
 {
-       struct tps65910_reg *tps65910_reg = platform_get_drvdata(pdev);
+       struct tps65910_reg *pmic = platform_get_drvdata(pdev);
        int i;
 
-       for (i = 0; i < TPS65910_NUM_REGULATOR; i++)
-               regulator_unregister(tps65910_reg->rdev[i]);
+       for (i = 0; i < pmic->num_regulators; i++)
+               regulator_unregister(pmic->rdev[i]);
 
-       kfree(tps65910_reg);
+       kfree(pmic->rdev);
+       kfree(pmic->info);
+       kfree(pmic->desc);
+       kfree(pmic);
        return 0;
 }
 
index 87fe0f75a56eed01375e0e2a393dbea6c1cd98b2..ee8747f4fa08b187ef2f79dbb51e3f69a770bc21 100644 (file)
@@ -835,8 +835,8 @@ static struct regulator_ops twlsmps_ops = {
                        remap_conf) \
                TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
                        remap_conf, TWL4030, twl4030fixed_ops)
-#define TWL6030_FIXED_LDO(label, offset, mVolts, num, turnon_delay) \
-               TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
+#define TWL6030_FIXED_LDO(label, offset, mVolts, turnon_delay) \
+               TWL_FIXED_LDO(label, offset, mVolts, 0x0, turnon_delay, \
                        0x0, TWL6030, twl6030fixed_ops)
 
 #define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) { \
@@ -856,24 +856,22 @@ static struct regulator_ops twlsmps_ops = {
                }, \
        }
 
-#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \
+#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \
        .base = offset, \
-       .id = num, \
        .min_mV = min_mVolts, \
        .max_mV = max_mVolts, \
        .desc = { \
                .name = #label, \
                .id = TWL6030_REG_##label, \
-               .n_voltages = (max_mVolts - min_mVolts)/100, \
+               .n_voltages = (max_mVolts - min_mVolts)/100 + 1, \
                .ops = &twl6030ldo_ops, \
                .type = REGULATOR_VOLTAGE, \
                .owner = THIS_MODULE, \
                }, \
        }
 
-#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \
+#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \
        .base = offset, \
-       .id = num, \
        .min_mV = min_mVolts, \
        .max_mV = max_mVolts, \
        .desc = { \
@@ -903,9 +901,8 @@ static struct regulator_ops twlsmps_ops = {
                }, \
        }
 
-#define TWL6030_FIXED_RESOURCE(label, offset, num, turnon_delay) { \
+#define TWL6030_FIXED_RESOURCE(label, offset, turnon_delay) { \
        .base = offset, \
-       .id = num, \
        .delay = turnon_delay, \
        .desc = { \
                .name = #label, \
@@ -916,9 +913,8 @@ static struct regulator_ops twlsmps_ops = {
                }, \
        }
 
-#define TWL6025_ADJUSTABLE_SMPS(label, offset, num) { \
+#define TWL6025_ADJUSTABLE_SMPS(label, offset) { \
        .base = offset, \
-       .id = num, \
        .min_mV = 600, \
        .max_mV = 2100, \
        .desc = { \
@@ -961,32 +957,32 @@ 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, 1000, 3300, 1),
-       TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300, 2),
-       TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300, 3),
-       TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300, 4),
-       TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300, 5),
-       TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300, 7),
-       TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0),
-       TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0),
-       TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0),
-       TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0),
-       TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 48, 0),
+       TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300),
+       TWL6030_FIXED_LDO(VANA, 0x50, 2100, 0),
+       TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 0),
+       TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 0),
+       TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 0),
+       TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 0),
 
        /* 6025 are renamed compared to 6030 versions */
-       TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300, 1),
-       TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300, 2),
-       TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300, 3),
-       TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300, 4),
-       TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300, 5),
-       TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300, 7),
-       TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300, 16),
-       TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300, 17),
-       TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300, 18),
-
-       TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34, 1),
-       TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10, 2),
-       TWL6025_ADJUSTABLE_SMPS(VIO, 0x16, 3),
+       TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300),
+
+       TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34),
+       TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10),
+       TWL6025_ADJUSTABLE_SMPS(VIO, 0x16),
 };
 
 static u8 twl_get_smps_offset(void)
index a0982e80985198004ae990218c38718d8616eec6..bd3531d8b2ac9364fb48c182c4947a00696d7c34 100644 (file)
@@ -267,23 +267,6 @@ static int wm831x_buckv_select_min_voltage(struct regulator_dev *rdev,
        return vsel;
 }
 
-static int wm831x_buckv_select_max_voltage(struct regulator_dev *rdev,
-                                          int min_uV, int max_uV)
-{
-       u16 vsel;
-
-       if (max_uV < 600000 || max_uV > 1800000)
-               return -EINVAL;
-
-       vsel = ((max_uV - 600000) / 12500) + 8;
-
-       if (wm831x_buckv_list_voltage(rdev, vsel) < min_uV ||
-           wm831x_buckv_list_voltage(rdev, vsel) < max_uV)
-               return -EINVAL;
-
-       return vsel;
-}
-
 static int wm831x_buckv_set_dvs(struct regulator_dev *rdev, int state)
 {
        struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
@@ -338,28 +321,23 @@ static int wm831x_buckv_set_voltage(struct regulator_dev *rdev,
        if (ret < 0)
                return ret;
 
-       /* Set the high voltage as the DVS voltage.  This is optimised
-        * for CPUfreq usage, most processors will keep the maximum
-        * voltage constant and lower the minimum with the frequency. */
-       vsel = wm831x_buckv_select_max_voltage(rdev, min_uV, max_uV);
-       if (vsel < 0) {
-               /* This should never happen - at worst the same vsel
-                * should be chosen */
-               WARN_ON(vsel < 0);
-               return 0;
+       /*
+        * If this VSEL is higher than the last one we've seen then
+        * remember it as the DVS VSEL.  This is optimised for CPUfreq
+        * usage where we want to get to the highest voltage very
+        * quickly.
+        */
+       if (vsel > dcdc->dvs_vsel) {
+               ret = wm831x_set_bits(wm831x, dvs_reg,
+                                     WM831X_DC1_DVS_VSEL_MASK,
+                                     dcdc->dvs_vsel);
+               if (ret == 0)
+                       dcdc->dvs_vsel = vsel;
+               else
+                       dev_warn(wm831x->dev,
+                                "Failed to set DCDC DVS VSEL: %d\n", ret);
        }
 
-       /* Don't bother if it's the same VSEL we're already using */
-       if (vsel == dcdc->on_vsel)
-               return 0;
-
-       ret = wm831x_set_bits(wm831x, dvs_reg, WM831X_DC1_DVS_VSEL_MASK, vsel);
-       if (ret == 0)
-               dcdc->dvs_vsel = vsel;
-       else
-               dev_warn(wm831x->dev, "Failed to set DCDC DVS VSEL: %d\n",
-                        ret);
-
        return 0;
 }
 
@@ -456,27 +434,6 @@ static __devinit void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc,
        if (!pdata || !pdata->dvs_gpio)
                return;
 
-       switch (pdata->dvs_control_src) {
-       case 1:
-               ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT;
-               break;
-       case 2:
-               ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT;
-               break;
-       default:
-               dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n",
-                       pdata->dvs_control_src, dcdc->name);
-               return;
-       }
-
-       ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL,
-                             WM831X_DC1_DVS_SRC_MASK, ctrl);
-       if (ret < 0) {
-               dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n",
-                       dcdc->name, ret);
-               return;
-       }
-
        ret = gpio_request(pdata->dvs_gpio, "DCDC DVS");
        if (ret < 0) {
                dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %d\n",
@@ -498,17 +455,57 @@ static __devinit void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc,
        }
 
        dcdc->dvs_gpio = pdata->dvs_gpio;
+
+       switch (pdata->dvs_control_src) {
+       case 1:
+               ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT;
+               break;
+       case 2:
+               ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT;
+               break;
+       default:
+               dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n",
+                       pdata->dvs_control_src, dcdc->name);
+               return;
+       }
+
+       /* If DVS_VSEL is set to the minimum value then raise it to ON_VSEL
+        * to make bootstrapping a bit smoother.
+        */
+       if (!dcdc->dvs_vsel) {
+               ret = wm831x_set_bits(wm831x,
+                                     dcdc->base + WM831X_DCDC_DVS_CONTROL,
+                                     WM831X_DC1_DVS_VSEL_MASK, dcdc->on_vsel);
+               if (ret == 0)
+                       dcdc->dvs_vsel = dcdc->on_vsel;
+               else
+                       dev_warn(wm831x->dev, "Failed to set DVS_VSEL: %d\n",
+                                ret);
+       }
+
+       ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL,
+                             WM831X_DC1_DVS_SRC_MASK, ctrl);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n",
+                       dcdc->name, ret);
+       }
 }
 
 static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
 {
        struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
        struct wm831x_pdata *pdata = wm831x->dev->platform_data;
-       int id = pdev->id % ARRAY_SIZE(pdata->dcdc);
+       int id;
        struct wm831x_dcdc *dcdc;
        struct resource *res;
        int ret, irq;
 
+       if (pdata && pdata->wm831x_num)
+               id = (pdata->wm831x_num * 10) + 1;
+       else
+               id = 0;
+       id = pdev->id - id;
+
        dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1);
 
        if (pdata == NULL || pdata->dcdc[id] == NULL)
@@ -545,7 +542,7 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
        }
        dcdc->on_vsel = ret & WM831X_DC1_ON_VSEL_MASK;
 
-       ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG);
+       ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL);
        if (ret < 0) {
                dev_err(wm831x->dev, "Failed to read DVS VSEL: %d\n", ret);
                goto err;
@@ -709,11 +706,17 @@ static __devinit int wm831x_buckp_probe(struct platform_device *pdev)
 {
        struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
        struct wm831x_pdata *pdata = wm831x->dev->platform_data;
-       int id = pdev->id % ARRAY_SIZE(pdata->dcdc);
+       int id;
        struct wm831x_dcdc *dcdc;
        struct resource *res;
        int ret, irq;
 
+       if (pdata && pdata->wm831x_num)
+               id = (pdata->wm831x_num * 10) + 1;
+       else
+               id = 0;
+       id = pdev->id - id;
+
        dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1);
 
        if (pdata == NULL || pdata->dcdc[id] == NULL)
@@ -1046,3 +1049,4 @@ MODULE_DESCRIPTION("WM831x DC-DC convertor driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:wm831x-buckv");
 MODULE_ALIAS("platform:wm831x-buckp");
+MODULE_ALIAS("platform:wm831x-epe");
index 2220cf8defb1625a1c161f026cf5722fd7356ea2..6709710a059e6e78c2267c376903b04ea26820fa 100644 (file)
@@ -310,11 +310,17 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)
 {
        struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
        struct wm831x_pdata *pdata = wm831x->dev->platform_data;
-       int id = pdev->id % ARRAY_SIZE(pdata->ldo);
+       int id;
        struct wm831x_ldo *ldo;
        struct resource *res;
        int ret, irq;
 
+       if (pdata && pdata->wm831x_num)
+               id = (pdata->wm831x_num * 10) + 1;
+       else
+               id = 0;
+       id = pdev->id - id;
+
        dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
 
        if (pdata == NULL || pdata->ldo[id] == NULL)
@@ -574,11 +580,17 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev)
 {
        struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
        struct wm831x_pdata *pdata = wm831x->dev->platform_data;
-       int id = pdev->id % ARRAY_SIZE(pdata->ldo);
+       int id;
        struct wm831x_ldo *ldo;
        struct resource *res;
        int ret, irq;
 
+       if (pdata && pdata->wm831x_num)
+               id = (pdata->wm831x_num * 10) + 1;
+       else
+               id = 0;
+       id = pdev->id - id;
+
        dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
 
        if (pdata == NULL || pdata->ldo[id] == NULL)
@@ -764,11 +776,18 @@ static __devinit int wm831x_alive_ldo_probe(struct platform_device *pdev)
 {
        struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
        struct wm831x_pdata *pdata = wm831x->dev->platform_data;
-       int id = pdev->id % ARRAY_SIZE(pdata->ldo);
+       int id;
        struct wm831x_ldo *ldo;
        struct resource *res;
        int ret;
 
+       if (pdata && pdata->wm831x_num)
+               id = (pdata->wm831x_num * 10) + 1;
+       else
+               id = 0;
+       id = pdev->id - id;
+
+
        dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
 
        if (pdata == NULL || pdata->ldo[id] == NULL)
index 35b2958d51064a84950deef280a14603b1e4e0da..1a6a690f24dbecc415c7c7f9c5006573a767ef0b 100644 (file)
@@ -43,7 +43,7 @@ static int wm8994_ldo_enable(struct regulator_dev *rdev)
        if (!ldo->enable)
                return 0;
 
-       gpio_set_value(ldo->enable, 1);
+       gpio_set_value_cansleep(ldo->enable, 1);
        ldo->is_enabled = true;
 
        return 0;
@@ -57,7 +57,7 @@ static int wm8994_ldo_disable(struct regulator_dev *rdev)
        if (!ldo->enable)
                return -EINVAL;
 
-       gpio_set_value(ldo->enable, 0);
+       gpio_set_value_cansleep(ldo->enable, 0);
        ldo->is_enabled = false;
 
        return 0;
index bcae8dd41496ab089d43e6639fedfd4272dca035..7789002bdd5c1af65f7c6bff95649e9e72d89db1 100644 (file)
@@ -368,7 +368,7 @@ static int __init omap_rtc_probe(struct platform_device *pdev)
                pr_info("%s: already running\n", pdev->name);
 
        /* force to 24 hour mode */
-       new_ctrl = reg & ~(OMAP_RTC_CTRL_SPLIT|OMAP_RTC_CTRL_AUTO_COMP);
+       new_ctrl = reg & (OMAP_RTC_CTRL_SPLIT|OMAP_RTC_CTRL_AUTO_COMP);
        new_ctrl |= OMAP_RTC_CTRL_STOP;
 
        /* BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE:
index 432444af7ee47b0192e08bddaee479e601c5c350..a1d3ddba99ccad4b90d4a9a06af1fd371b2d0033 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/mutex.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
+#include <linux/vmalloc.h>
 
 #include <asm/ccwdev.h>
 #include <asm/ebcdic.h>
@@ -888,11 +889,11 @@ char *dasd_get_user_string(const char __user *user_buf, size_t user_len)
 {
        char *buffer;
 
-       buffer = kmalloc(user_len + 1, GFP_KERNEL);
+       buffer = vmalloc(user_len + 1);
        if (buffer == NULL)
                return ERR_PTR(-ENOMEM);
        if (copy_from_user(buffer, user_buf, user_len) != 0) {
-               kfree(buffer);
+               vfree(buffer);
                return ERR_PTR(-EFAULT);
        }
        /* got the string, now strip linefeed. */
@@ -930,7 +931,7 @@ static ssize_t dasd_stats_write(struct file *file,
                dasd_profile_off(prof);
        } else
                rc = -EINVAL;
-       kfree(buffer);
+       vfree(buffer);
        return rc;
 }
 
@@ -1042,7 +1043,7 @@ static ssize_t dasd_stats_global_write(struct file *file,
                dasd_global_profile_level = DASD_PROFILE_OFF;
        } else
                rc = -EINVAL;
-       kfree(buffer);
+       vfree(buffer);
        return rc;
 }
 
index 30fb979d684d0043d2b1f6b0aff2fda6ebe8cfcd..6e835c9fdfcba9b5c6e9e501d6921aefa266c012 100644 (file)
@@ -1461,6 +1461,15 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
                                "Read device characteristic failed, rc=%d", rc);
                goto out_err3;
        }
+
+       if ((device->features & DASD_FEATURE_USERAW) &&
+           !(private->rdc_data.facilities.RT_in_LR)) {
+               dev_err(&device->cdev->dev, "The storage server does not "
+                       "support raw-track access\n");
+               rc = -EINVAL;
+               goto out_err3;
+       }
+
        /* find the valid cylinder size */
        if (private->rdc_data.no_cyl == LV_COMPAT_CYL &&
            private->rdc_data.long_no_cyl)
index 6c3c5364d0824426cf4a7db5db7bd588804ebbc5..e12989fff4ffca7f394124a814a176b82985b941 100644 (file)
@@ -312,14 +312,14 @@ static ssize_t dasd_stats_proc_write(struct file *file,
                pr_info("The statistics have been reset\n");
        } else
                goto out_parse_error;
-       kfree(buffer);
+       vfree(buffer);
        return user_len;
 out_parse_error:
        rc = -EINVAL;
        pr_warning("%s is not a supported value for /proc/dasd/statistics\n",
                str);
 out_error:
-       kfree(buffer);
+       vfree(buffer);
        return rc;
 #else
        pr_warning("/proc/dasd/statistics: is not activated in this kernel\n");
index 7ad30e72f868de1e45ef7e4bdd81cfa8a4701ca4..5f9f929e891c99ac6bc04e74a367ca0e56e76570 100644 (file)
@@ -82,12 +82,9 @@ static int proc_handler_callhome(struct ctl_table *ctl, int write,
                        return -EFAULT;
        } else {
                len = *count;
-               rc = copy_from_user(buf, buffer, sizeof(buf));
-               if (rc != 0)
-                       return -EFAULT;
-               buf[sizeof(buf) - 1] = '\0';
-               if (strict_strtoul(buf, 0, &val) != 0)
-                       return -EINVAL;
+               rc = kstrtoul_from_user(buffer, len, 0, &val);
+               if (rc)
+                       return rc;
                if (val != 0 && val != 1)
                        return -EINVAL;
                callhome_enabled = val;
index 7bc643f3f5ab1956b1480b8eece886e565fbf940..e5c966462c5ad2ae9001e6a4df863c8133feb46c 100644 (file)
@@ -14,6 +14,8 @@
 #include "chsc.h"
 
 #define QDIO_BUSY_BIT_PATIENCE         (100 << 12)     /* 100 microseconds */
+#define QDIO_BUSY_BIT_RETRY_DELAY      10              /* 10 milliseconds */
+#define QDIO_BUSY_BIT_RETRIES          1000            /* = 10s retry time */
 #define QDIO_INPUT_THRESHOLD           (500 << 12)     /* 500 microseconds */
 
 /*
index f8b03a636e492ad6a525e8307e44e7fb6aeaccef..0e615cb912d0eb0a90a2d510b5814d3586b06297 100644 (file)
@@ -188,19 +188,13 @@ static ssize_t qperf_seq_write(struct file *file, const char __user *ubuf,
        struct qdio_irq *irq_ptr = seq->private;
        struct qdio_q *q;
        unsigned long val;
-       char buf[8];
        int ret, i;
 
        if (!irq_ptr)
                return 0;
-       if (count >= sizeof(buf))
-               return -EINVAL;
-       if (copy_from_user(&buf, ubuf, count))
-               return -EFAULT;
-       buf[count] = 0;
-
-       ret = strict_strtoul(buf, 10, &val);
-       if (ret < 0)
+
+       ret = kstrtoul_from_user(ubuf, count, 10, &val);
+       if (ret)
                return ret;
 
        switch (val) {
index e58169c32474f41b436d3ad8259efed71af3121e..288c9140290e9a08b899b234c7e514c9bcc6838a 100644 (file)
@@ -313,7 +313,7 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit)
        unsigned long schid = *((u32 *) &q->irq_ptr->schid);
        unsigned int fc = QDIO_SIGA_WRITE;
        u64 start_time = 0;
-       int cc;
+       int retries = 0, cc;
 
        if (is_qebsm(q)) {
                schid = q->irq_ptr->sch_token;
@@ -325,6 +325,7 @@ again:
        /* hipersocket busy condition */
        if (unlikely(*busy_bit)) {
                WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2);
+               retries++;
 
                if (!start_time) {
                        start_time = get_clock();
@@ -333,6 +334,11 @@ again:
                if ((get_clock() - start_time) < QDIO_BUSY_BIT_PATIENCE)
                        goto again;
        }
+       if (retries) {
+               DBF_DEV_EVENT(DBF_WARN, q->irq_ptr,
+                             "%4x cc2 BB1:%1d", SCH_NO(q), q->nr);
+               DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "count:%u", retries);
+       }
        return cc;
 }
 
@@ -728,13 +734,14 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q)
 
 static int qdio_kick_outbound_q(struct qdio_q *q)
 {
+       int retries = 0, cc;
        unsigned int busy_bit;
-       int cc;
 
        if (!need_siga_out(q))
                return 0;
 
        DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr);
+retry:
        qperf_inc(q, siga_write);
 
        cc = qdio_siga_output(q, &busy_bit);
@@ -743,7 +750,11 @@ static int qdio_kick_outbound_q(struct qdio_q *q)
                break;
        case 2:
                if (busy_bit) {
-                       DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr);
+                       while (++retries < QDIO_BUSY_BIT_RETRIES) {
+                               mdelay(QDIO_BUSY_BIT_RETRY_DELAY);
+                               goto retry;
+                       }
+                       DBF_ERROR("%4x cc2 BBC:%1d", SCH_NO(q), q->nr);
                        cc |= QDIO_ERROR_SIGA_BUSY;
                } else
                        DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d", q->nr);
@@ -753,6 +764,10 @@ static int qdio_kick_outbound_q(struct qdio_q *q)
                DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc);
                break;
        }
+       if (retries) {
+               DBF_ERROR("%4x cc2 BB2:%1d", SCH_NO(q), q->nr);
+               DBF_ERROR("count:%u", retries);
+       }
        return cc;
 }
 
index eba88c749fb1db06343ac9afac9490ade3e1a6b1..730b4a37b82370805bb6da7c380952178f71cd1a 100644 (file)
@@ -2267,17 +2267,13 @@ static int __devexit
 pl022_remove(struct amba_device *adev)
 {
        struct pl022 *pl022 = amba_get_drvdata(adev);
-       int status = 0;
+
        if (!pl022)
                return 0;
 
        /* Remove the queue */
-       status = destroy_queue(pl022);
-       if (status != 0) {
-               dev_err(&adev->dev,
-                       "queue remove failed (%d)\n", status);
-               return status;
-       }
+       if (destroy_queue(pl022) != 0)
+               dev_err(&adev->dev, "queue remove failed\n");
        load_ssp_default_config(pl022);
        pl022_dma_remove(pl022);
        free_irq(adev->irq[0], pl022);
@@ -2289,7 +2285,6 @@ pl022_remove(struct amba_device *adev)
        spi_unregister_master(pl022->master);
        spi_master_put(pl022->master);
        amba_set_drvdata(adev, NULL);
-       dev_dbg(&adev->dev, "remove succeeded\n");
        return 0;
 }
 
index 564ff4e0dbc47ed6e1d6f644dd7486043ed90870..8345fb457a40740cc1e381d6a89b7e69abb1b1ae 100644 (file)
@@ -1,5 +1,6 @@
 config ISCSI_TARGET
        tristate "Linux-iSCSI.org iSCSI Target Mode Stack"
+       depends on NET
        select CRYPTO
        select CRYPTO_CRC32C
        select CRYPTO_CRC32C_INTEL if X86
index 14c81c4265bd912dd4c4113c819d2db39aa58894..c24fb10de60be4912962f9649feb2e21a96d231b 100644 (file)
@@ -120,7 +120,7 @@ struct iscsi_tiqn *iscsit_add_tiqn(unsigned char *buf)
        struct iscsi_tiqn *tiqn = NULL;
        int ret;
 
-       if (strlen(buf) > ISCSI_IQN_LEN) {
+       if (strlen(buf) >= ISCSI_IQN_LEN) {
                pr_err("Target IQN exceeds %d bytes\n",
                                ISCSI_IQN_LEN);
                return ERR_PTR(-EINVAL);
@@ -1857,7 +1857,7 @@ static int iscsit_handle_text_cmd(
        char *text_ptr, *text_in;
        int cmdsn_ret, niov = 0, rx_got, rx_size;
        u32 checksum = 0, data_crc = 0, payload_length;
-       u32 padding = 0, text_length = 0;
+       u32 padding = 0, pad_bytes = 0, text_length = 0;
        struct iscsi_cmd *cmd;
        struct kvec iov[3];
        struct iscsi_text *hdr;
@@ -1896,7 +1896,7 @@ static int iscsit_handle_text_cmd(
 
                padding = ((-payload_length) & 3);
                if (padding != 0) {
-                       iov[niov].iov_base = cmd->pad_bytes;
+                       iov[niov].iov_base = &pad_bytes;
                        iov[niov++].iov_len  = padding;
                        rx_size += padding;
                        pr_debug("Receiving %u additional bytes"
@@ -1917,7 +1917,7 @@ static int iscsit_handle_text_cmd(
                if (conn->conn_ops->DataDigest) {
                        iscsit_do_crypto_hash_buf(&conn->conn_rx_hash,
                                        text_in, text_length,
-                                       padding, cmd->pad_bytes,
+                                       padding, (u8 *)&pad_bytes,
                                        (u8 *)&data_crc);
 
                        if (checksum != data_crc) {
@@ -3468,7 +3468,12 @@ static inline void iscsit_thread_check_cpumask(
 }
 
 #else
-#define iscsit_thread_get_cpumask(X) ({})
+
+void iscsit_thread_get_cpumask(struct iscsi_conn *conn)
+{
+       return;
+}
+
 #define iscsit_thread_check_cpumask(X, Y, Z) ({})
 #endif /* CONFIG_SMP */
 
index 32bb92c44450bb4eeebc34ebe73fa391233d72de..f095e65b1ccf401a2a4b951d5e8c463827dcc57f 100644 (file)
@@ -181,7 +181,7 @@ struct se_tpg_np *lio_target_call_addnptotpg(
                return ERR_PTR(-EOVERFLOW);
        }
        memset(buf, 0, MAX_PORTAL_LEN + 1);
-       snprintf(buf, MAX_PORTAL_LEN, "%s", name);
+       snprintf(buf, MAX_PORTAL_LEN + 1, "%s", name);
 
        memset(&sockaddr, 0, sizeof(struct __kernel_sockaddr_storage));
 
index 713a4d23557a9e6e5c8d0817b83eb143da5ba258..4d087ac110675cd531619ae1e0b60a037df37b2e 100644 (file)
@@ -978,7 +978,7 @@ struct iscsi_login *iscsi_target_init_negotiation(
                pr_err("Unable to allocate memory for struct iscsi_login.\n");
                iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
                                ISCSI_LOGIN_STATUS_NO_RESOURCES);
-               goto out;
+               return NULL;
        }
 
        login->req = kzalloc(ISCSI_HDR_LEN, GFP_KERNEL);
index c75a01a1c4757976b776634f0fe14d00aa35b313..89760329d5d0d292c0c8648c8aff16495a1822b4 100644 (file)
@@ -1747,6 +1747,8 @@ int transport_generic_handle_cdb(
 }
 EXPORT_SYMBOL(transport_generic_handle_cdb);
 
+static void transport_generic_request_failure(struct se_cmd *,
+                       struct se_device *, int, int);
 /*
  * Used by fabric module frontends to queue tasks directly.
  * Many only be used from process context only
@@ -1754,6 +1756,8 @@ EXPORT_SYMBOL(transport_generic_handle_cdb);
 int transport_handle_cdb_direct(
        struct se_cmd *cmd)
 {
+       int ret;
+
        if (!cmd->se_lun) {
                dump_stack();
                pr_err("cmd->se_lun is NULL\n");
@@ -1765,8 +1769,31 @@ int transport_handle_cdb_direct(
                                " from interrupt context\n");
                return -EINVAL;
        }
-
-       return transport_generic_new_cmd(cmd);
+       /*
+        * Set TRANSPORT_NEW_CMD state and cmd->t_transport_active=1 following
+        * transport_generic_handle_cdb*() -> transport_add_cmd_to_queue()
+        * in existing usage to ensure that outstanding descriptors are handled
+        * correctly during shutdown via transport_generic_wait_for_tasks()
+        *
+        * Also, we don't take cmd->t_state_lock here as we only expect
+        * this to be called for initial descriptor submission.
+        */
+       cmd->t_state = TRANSPORT_NEW_CMD;
+       atomic_set(&cmd->t_transport_active, 1);
+       /*
+        * transport_generic_new_cmd() is already handling QUEUE_FULL,
+        * so follow TRANSPORT_NEW_CMD processing thread context usage
+        * and call transport_generic_request_failure() if necessary..
+        */
+       ret = transport_generic_new_cmd(cmd);
+       if (ret == -EAGAIN)
+               return 0;
+       else if (ret < 0) {
+               cmd->transport_error_status = ret;
+               transport_generic_request_failure(cmd, NULL, 0,
+                               (cmd->data_direction != DMA_TO_DEVICE));
+       }
+       return 0;
 }
 EXPORT_SYMBOL(transport_handle_cdb_direct);
 
@@ -3324,7 +3351,7 @@ static int transport_generic_cmd_sequencer(
                        goto out_invalid_cdb_field;
                }
 
-               cmd->t_task_lba = get_unaligned_be16(&cdb[2]);
+               cmd->t_task_lba = get_unaligned_be64(&cdb[2]);
                passthrough = (dev->transport->transport_type ==
                                TRANSPORT_PLUGIN_PHBA_PDEV);
                /*
index f7fff7ed63c36ac98fa5eaa01439a3d20d1afb37..bd4fe21a23b889d8083cb57c32ae4fa588b2ed69 100644 (file)
@@ -187,4 +187,9 @@ void ft_dump_cmd(struct ft_cmd *, const char *caller);
 
 ssize_t ft_format_wwn(char *, size_t, u64);
 
+/*
+ * Underlying HW specific helper function
+ */
+void ft_invl_hw_context(struct ft_cmd *);
+
 #endif /* __TCM_FC_H__ */
index 09df38b4610ce6f5d4abf6b8df844bad51acc425..5654dc22f7aef0adb2cbedb301057d9e5cc4ca69 100644 (file)
@@ -320,6 +320,7 @@ static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg)
        default:
                pr_debug("%s: unhandled frame r_ctl %x\n",
                       __func__, fh->fh_r_ctl);
+               ft_invl_hw_context(cmd);
                fc_frame_free(fp);
                transport_generic_free_cmd(&cmd->se_cmd, 0, 0);
                break;
index 8e2a46ddcccb5b5ce808b1545e7c91afb741d56a..c37f4cd96452e8bf6ab40d99ee45f7df84a49bcb 100644 (file)
@@ -213,62 +213,49 @@ void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame *fp)
        if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF))
                goto drop;
 
+       f_ctl = ntoh24(fh->fh_f_ctl);
+       ep = fc_seq_exch(seq);
+       lport = ep->lp;
+       if (cmd->was_ddp_setup) {
+               BUG_ON(!ep);
+               BUG_ON(!lport);
+       }
+
        /*
-        * Doesn't expect even single byte of payload. Payload
+        * Doesn't expect payload if DDP is setup. Payload
         * is expected to be copied directly to user buffers
-        * due to DDP (Large Rx offload) feature, hence
-        * BUG_ON if BUF is non-NULL
+        * due to DDP (Large Rx offload),
         */
        buf = fc_frame_payload_get(fp, 1);
-       if (cmd->was_ddp_setup && buf) {
-               pr_debug("%s: When DDP was setup, not expected to"
-                                "receive frame with payload, Payload shall be"
-                                "copied directly to buffer instead of coming "
-                                "via. legacy receive queues\n", __func__);
-               BUG_ON(buf);
-       }
+       if (buf)
+               pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, "
+                               "cmd->sg_cnt 0x%x. DDP was setup"
+                               " hence not expected to receive frame with "
+                               "payload, Frame will be dropped if "
+                               "'Sequence Initiative' bit in f_ctl is "
+                               "not set\n", __func__, ep->xid, f_ctl,
+                               cmd->sg, cmd->sg_cnt);
+       /*
+        * Invalidate HW DDP context if it was setup for respective
+        * command. Invalidation of HW DDP context is requited in both
+        * situation (success and error). 
+        */
+       ft_invl_hw_context(cmd);
 
        /*
-        * If ft_cmd indicated 'ddp_setup', in that case only the last frame
-        * should come with 'TSI bit being set'. If 'TSI bit is not set and if
-        * data frame appears here, means error condition. In both the cases
-        * release the DDP context (ddp_put) and in error case, as well
-        * initiate error recovery mechanism.
+        * If "Sequence Initiative (TSI)" bit set in f_ctl, means last
+        * write data frame is received successfully where payload is
+        * posted directly to user buffer and only the last frame's
+        * header is posted in receive queue.
+        *
+        * If "Sequence Initiative (TSI)" bit is not set, means error
+        * condition w.r.t. DDP, hence drop the packet and let explict
+        * ABORTS from other end of exchange timer trigger the recovery.
         */
-       ep = fc_seq_exch(seq);
-       if (cmd->was_ddp_setup) {
-               BUG_ON(!ep);
-               lport = ep->lp;
-               BUG_ON(!lport);
-       }
-       if (cmd->was_ddp_setup && ep->xid != FC_XID_UNKNOWN) {
-               f_ctl = ntoh24(fh->fh_f_ctl);
-               /*
-                * If TSI bit set in f_ctl, means last write data frame is
-                * received successfully where payload is posted directly
-                * to user buffer and only the last frame's header is posted
-                * in legacy receive queue
-                */
-               if (f_ctl & FC_FC_SEQ_INIT) { /* TSI bit set in FC frame */
-                       cmd->write_data_len = lport->tt.ddp_done(lport,
-                                                               ep->xid);
-                       goto last_frame;
-               } else {
-                       /*
-                        * Updating the write_data_len may be meaningless at
-                        * this point, but just in case if required in future
-                        * for debugging or any other purpose
-                        */
-                       pr_err("%s: Received frame with TSI bit not"
-                                       " being SET, dropping the frame, "
-                                       "cmd->sg <%p>, cmd->sg_cnt <0x%x>\n",
-                                       __func__, cmd->sg, cmd->sg_cnt);
-                       cmd->write_data_len = lport->tt.ddp_done(lport,
-                                                             ep->xid);
-                       lport->tt.seq_exch_abort(cmd->seq, 0);
-                       goto drop;
-               }
-       }
+       if (f_ctl & FC_FC_SEQ_INIT)
+               goto last_frame;
+       else
+               goto drop;
 
        rel_off = ntohl(fh->fh_parm_offset);
        frame_len = fr_len(fp);
@@ -331,3 +318,39 @@ last_frame:
 drop:
        fc_frame_free(fp);
 }
+
+/*
+ * Handle and cleanup any HW specific resources if
+ * received ABORTS, errors, timeouts.
+ */
+void ft_invl_hw_context(struct ft_cmd *cmd)
+{
+       struct fc_seq *seq = cmd->seq;
+       struct fc_exch *ep = NULL;
+       struct fc_lport *lport = NULL;
+
+       BUG_ON(!cmd);
+
+       /* Cleanup the DDP context in HW if DDP was setup */
+       if (cmd->was_ddp_setup && seq) {
+               ep = fc_seq_exch(seq);
+               if (ep) {
+                       lport = ep->lp;
+                       if (lport && (ep->xid <= lport->lro_xid))
+                               /*
+                                * "ddp_done" trigger invalidation of HW
+                                * specific DDP context
+                                */
+                               cmd->write_data_len = lport->tt.ddp_done(lport,
+                                                                     ep->xid);
+
+                               /*
+                                * Resetting same variable to indicate HW's
+                                * DDP context has been invalidated to avoid
+                                * re_invalidation of same context (context is
+                                * identified using ep->xid)
+                                */
+                               cmd->was_ddp_setup = 0;
+               }
+       }
+}
index bf7c687519ef4026ea2a4a7b6506287a47843d6e..f7f71b2d3101750fe0a4f2d1fd68dd16aad14269 100644 (file)
@@ -14,11 +14,7 @@ menuconfig THERMAL
          If you want this support, you should say Y or M here.
 
 config THERMAL_HWMON
-       bool "Hardware monitoring support"
+       bool
        depends on THERMAL
        depends on HWMON=y || HWMON=THERMAL
-       help
-         The generic thermal sysfs driver's hardware monitoring support
-         requires a 2.10.7/3.0.2 or later lm-sensors userspace.
-
-         Say Y if your user-space is new enough.
+       default y
index 0b1c82ad6805d2e0088840c4d2d17cff7e5dd109..708f8e92771a6d03c6ef3615571898da0cd1321e 100644 (file)
@@ -420,6 +420,29 @@ thermal_cooling_device_trip_point_show(struct device *dev,
 
 /* hwmon sys I/F */
 #include <linux/hwmon.h>
+
+/* thermal zone devices with the same type share one hwmon device */
+struct thermal_hwmon_device {
+       char type[THERMAL_NAME_LENGTH];
+       struct device *device;
+       int count;
+       struct list_head tz_list;
+       struct list_head node;
+};
+
+struct thermal_hwmon_attr {
+       struct device_attribute attr;
+       char name[16];
+};
+
+/* one temperature input for each thermal zone */
+struct thermal_hwmon_temp {
+       struct list_head hwmon_node;
+       struct thermal_zone_device *tz;
+       struct thermal_hwmon_attr temp_input;   /* hwmon sys attr */
+       struct thermal_hwmon_attr temp_crit;    /* hwmon sys attr */
+};
+
 static LIST_HEAD(thermal_hwmon_list);
 
 static ssize_t
@@ -437,9 +460,10 @@ temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
        int ret;
        struct thermal_hwmon_attr *hwmon_attr
                        = container_of(attr, struct thermal_hwmon_attr, attr);
-       struct thermal_zone_device *tz
-                       = container_of(hwmon_attr, struct thermal_zone_device,
+       struct thermal_hwmon_temp *temp
+                       = container_of(hwmon_attr, struct thermal_hwmon_temp,
                                       temp_input);
+       struct thermal_zone_device *tz = temp->tz;
 
        ret = tz->ops->get_temp(tz, &temperature);
 
@@ -455,9 +479,10 @@ temp_crit_show(struct device *dev, struct device_attribute *attr,
 {
        struct thermal_hwmon_attr *hwmon_attr
                        = container_of(attr, struct thermal_hwmon_attr, attr);
-       struct thermal_zone_device *tz
-                       = container_of(hwmon_attr, struct thermal_zone_device,
+       struct thermal_hwmon_temp *temp
+                       = container_of(hwmon_attr, struct thermal_hwmon_temp,
                                       temp_crit);
+       struct thermal_zone_device *tz = temp->tz;
        long temperature;
        int ret;
 
@@ -469,22 +494,54 @@ temp_crit_show(struct device *dev, struct device_attribute *attr,
 }
 
 
-static int
-thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
+static struct thermal_hwmon_device *
+thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
 {
        struct thermal_hwmon_device *hwmon;
-       int new_hwmon_device = 1;
-       int result;
 
        mutex_lock(&thermal_list_lock);
        list_for_each_entry(hwmon, &thermal_hwmon_list, node)
                if (!strcmp(hwmon->type, tz->type)) {
-                       new_hwmon_device = 0;
                        mutex_unlock(&thermal_list_lock);
-                       goto register_sys_interface;
+                       return hwmon;
+               }
+       mutex_unlock(&thermal_list_lock);
+
+       return NULL;
+}
+
+/* Find the temperature input matching a given thermal zone */
+static struct thermal_hwmon_temp *
+thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
+                         const struct thermal_zone_device *tz)
+{
+       struct thermal_hwmon_temp *temp;
+
+       mutex_lock(&thermal_list_lock);
+       list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
+               if (temp->tz == tz) {
+                       mutex_unlock(&thermal_list_lock);
+                       return temp;
                }
        mutex_unlock(&thermal_list_lock);
 
+       return NULL;
+}
+
+static int
+thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
+{
+       struct thermal_hwmon_device *hwmon;
+       struct thermal_hwmon_temp *temp;
+       int new_hwmon_device = 1;
+       int result;
+
+       hwmon = thermal_hwmon_lookup_by_type(tz);
+       if (hwmon) {
+               new_hwmon_device = 0;
+               goto register_sys_interface;
+       }
+
        hwmon = kzalloc(sizeof(struct thermal_hwmon_device), GFP_KERNEL);
        if (!hwmon)
                return -ENOMEM;
@@ -502,30 +559,36 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
                goto free_mem;
 
  register_sys_interface:
-       tz->hwmon = hwmon;
+       temp = kzalloc(sizeof(struct thermal_hwmon_temp), GFP_KERNEL);
+       if (!temp) {
+               result = -ENOMEM;
+               goto unregister_name;
+       }
+
+       temp->tz = tz;
        hwmon->count++;
 
-       snprintf(tz->temp_input.name, THERMAL_NAME_LENGTH,
+       snprintf(temp->temp_input.name, THERMAL_NAME_LENGTH,
                 "temp%d_input", hwmon->count);
-       tz->temp_input.attr.attr.name = tz->temp_input.name;
-       tz->temp_input.attr.attr.mode = 0444;
-       tz->temp_input.attr.show = temp_input_show;
-       sysfs_attr_init(&tz->temp_input.attr.attr);
-       result = device_create_file(hwmon->device, &tz->temp_input.attr);
+       temp->temp_input.attr.attr.name = temp->temp_input.name;
+       temp->temp_input.attr.attr.mode = 0444;
+       temp->temp_input.attr.show = temp_input_show;
+       sysfs_attr_init(&temp->temp_input.attr.attr);
+       result = device_create_file(hwmon->device, &temp->temp_input.attr);
        if (result)
-               goto unregister_name;
+               goto free_temp_mem;
 
        if (tz->ops->get_crit_temp) {
                unsigned long temperature;
                if (!tz->ops->get_crit_temp(tz, &temperature)) {
-                       snprintf(tz->temp_crit.name, THERMAL_NAME_LENGTH,
+                       snprintf(temp->temp_crit.name, THERMAL_NAME_LENGTH,
                                "temp%d_crit", hwmon->count);
-                       tz->temp_crit.attr.attr.name = tz->temp_crit.name;
-                       tz->temp_crit.attr.attr.mode = 0444;
-                       tz->temp_crit.attr.show = temp_crit_show;
-                       sysfs_attr_init(&tz->temp_crit.attr.attr);
+                       temp->temp_crit.attr.attr.name = temp->temp_crit.name;
+                       temp->temp_crit.attr.attr.mode = 0444;
+                       temp->temp_crit.attr.show = temp_crit_show;
+                       sysfs_attr_init(&temp->temp_crit.attr.attr);
                        result = device_create_file(hwmon->device,
-                                                   &tz->temp_crit.attr);
+                                                   &temp->temp_crit.attr);
                        if (result)
                                goto unregister_input;
                }
@@ -534,13 +597,15 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
        mutex_lock(&thermal_list_lock);
        if (new_hwmon_device)
                list_add_tail(&hwmon->node, &thermal_hwmon_list);
-       list_add_tail(&tz->hwmon_node, &hwmon->tz_list);
+       list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
        mutex_unlock(&thermal_list_lock);
 
        return 0;
 
  unregister_input:
-       device_remove_file(hwmon->device, &tz->temp_input.attr);
+       device_remove_file(hwmon->device, &temp->temp_input.attr);
+ free_temp_mem:
+       kfree(temp);
  unregister_name:
        if (new_hwmon_device) {
                device_remove_file(hwmon->device, &dev_attr_name);
@@ -556,15 +621,30 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
 static void
 thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
 {
-       struct thermal_hwmon_device *hwmon = tz->hwmon;
+       struct thermal_hwmon_device *hwmon;
+       struct thermal_hwmon_temp *temp;
+
+       hwmon = thermal_hwmon_lookup_by_type(tz);
+       if (unlikely(!hwmon)) {
+               /* Should never happen... */
+               dev_dbg(&tz->device, "hwmon device lookup failed!\n");
+               return;
+       }
+
+       temp = thermal_hwmon_lookup_temp(hwmon, tz);
+       if (unlikely(!temp)) {
+               /* Should never happen... */
+               dev_dbg(&tz->device, "temperature input lookup failed!\n");
+               return;
+       }
 
-       tz->hwmon = NULL;
-       device_remove_file(hwmon->device, &tz->temp_input.attr);
+       device_remove_file(hwmon->device, &temp->temp_input.attr);
        if (tz->ops->get_crit_temp)
-               device_remove_file(hwmon->device, &tz->temp_crit.attr);
+               device_remove_file(hwmon->device, &temp->temp_crit.attr);
 
        mutex_lock(&thermal_list_lock);
-       list_del(&tz->hwmon_node);
+       list_del(&temp->hwmon_node);
+       kfree(temp);
        if (!list_empty(&hwmon->tz_list)) {
                mutex_unlock(&thermal_list_lock);
                return;
index 827db7654594e43c0b078ef7cb65894585f56261..7e91b3d368cd1ddc26e07e5a6de42b6b94752121 100644 (file)
@@ -1286,22 +1286,17 @@ static int serial_imx_resume(struct platform_device *dev)
 static int serial_imx_probe_dt(struct imx_port *sport,
                struct platform_device *pdev)
 {
+       static int portnum = 0;
        struct device_node *np = pdev->dev.of_node;
        const struct of_device_id *of_id =
                        of_match_device(imx_uart_dt_ids, &pdev->dev);
-       int ret;
 
        if (!np)
                return -ENODEV;
 
-       ret = of_alias_get_id(np, "serial");
-       if (ret < 0) {
-               pr_err("%s: failed to get alias id, errno %d\n",
-                       __func__, ret);
-               return -ENODEV;
-       } else {
-               sport->port.line = ret;
-       }
+       sport->port.line = portnum++;
+       if (sport->port.line >= UART_NR)
+               return -EINVAL;
 
        if (of_get_property(np, "fsl,uart-has-rtscts", NULL))
                sport->have_rtscts = 1;
index 69407e72aac1a9291d2ceb3dec812da2063cf94b..278aeaa925059673fdcefa7b1c243f5a92b41837 100644 (file)
@@ -336,7 +336,7 @@ config BACKLIGHT_PCF50633
          enable its driver.
 
 config BACKLIGHT_AAT2870
-       bool "AnalogicTech AAT2870 Backlight"
+       tristate "AnalogicTech AAT2870 Backlight"
        depends on BACKLIGHT_CLASS_DEVICE && MFD_AAT2870_CORE
        help
          If you have a AnalogicTech AAT2870 say Y to enable the
index 4952a617563d102010070cf2e580079495f5fddb..331f1ef1dad5c87984c813d465c06b84733896a8 100644 (file)
@@ -44,7 +44,7 @@ static inline int aat2870_brightness(struct aat2870_bl_driver_data *aat2870_bl,
        struct backlight_device *bd = aat2870_bl->bd;
        int val;
 
-       val = brightness * aat2870_bl->max_current;
+       val = brightness * (aat2870_bl->max_current - 1);
        val /= bd->props.max_brightness;
 
        return val;
@@ -158,10 +158,10 @@ static int aat2870_bl_probe(struct platform_device *pdev)
        props.type = BACKLIGHT_RAW;
        bd = backlight_device_register("aat2870-backlight", &pdev->dev,
                                       aat2870_bl, &aat2870_bl_ops, &props);
-       if (!bd) {
+       if (IS_ERR(bd)) {
                dev_err(&pdev->dev,
                        "Failed allocate memory for backlight device\n");
-               ret = -ENOMEM;
+               ret = PTR_ERR(bd);
                goto out_kfree;
        }
 
@@ -175,7 +175,7 @@ static int aat2870_bl_probe(struct platform_device *pdev)
        else
                aat2870_bl->channels = AAT2870_BL_CH_ALL;
 
-       if (pdata->max_brightness > 0)
+       if (pdata->max_current > 0)
                aat2870_bl->max_current = pdata->max_current;
        else
                aat2870_bl->max_current = AAT2870_CURRENT_27_9;
index 32549d177b198ae2f7f2b9a738d6b8a0cb9de049..dcaab9012ca20564dacbe33c6d00b305869252b2 100644 (file)
@@ -55,7 +55,7 @@
 
 #define S3_SAVAGE3D_SERIES(chip)  ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE_MX))
 
-#define S3_SAVAGE4_SERIES(chip)   ((chip>=S3_SAVAGE4) || (chip<=S3_PROSAVAGEDDR))
+#define S3_SAVAGE4_SERIES(chip)   ((chip>=S3_SAVAGE4) && (chip<=S3_PROSAVAGEDDR))
 
 #define S3_SAVAGE_MOBILE_SERIES(chip)  ((chip==S3_SAVAGE_MX) || (chip==S3_SUPERSAVAGE))
 
index f441726ddf2bbf2a988981e32066806720fde5c3..86b0735e6aa0fd652ace4ba8546e0a49245590f2 100644 (file)
@@ -36,9 +36,6 @@ config WATCHDOG_CORE
          and gives them the /dev/watchdog interface (and later also the
          sysfs interface).
 
-         To compile this driver as a module, choose M here: the module will
-         be called watchdog.
-
 config WATCHDOG_NOWAYOUT
        bool "Disable watchdog shutdown on close"
        help
index afa78a54711e1f51097eb9425ebe3f4b8fa0b72a..809f41c30c4433dfb3e45b53835935608f95753e 100644 (file)
@@ -458,7 +458,15 @@ static int __devexit nv_tco_remove(struct platform_device *dev)
 
 static void nv_tco_shutdown(struct platform_device *dev)
 {
+       u32 val;
+
        tco_timer_stop();
+
+       /* Some BIOSes fail the POST (once) if the NO_REBOOT flag is not
+        * unset during shutdown. */
+       pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val);
+       val &= ~MCP51_SMBUS_SETUP_B_TCO_REBOOT;
+       pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val);
 }
 
 static struct platform_driver nv_tco_driver = {
index db84f2322d1ad1ef4d69f12ed2dd2936a03d4b16..a267dc078dafb777f6855a56aef73ae48c1228f5 100644 (file)
@@ -64,7 +64,7 @@
  * misses its deadline, the kernel timer will allow the WDT to overflow.
  */
 static int clock_division_ratio = WTCSR_CKS_4096;
-#define next_ping_period(cks)  msecs_to_jiffies(cks - 4)
+#define next_ping_period(cks)  (jiffies + msecs_to_jiffies(cks - 4))
 
 static const struct watchdog_info sh_wdt_info;
 static struct platform_device *sh_wdt_dev;
index f815283667affd00f568fd16e8452d11ede21f89..5f7ff8e2fc1433c39229fce28d4fca39fe8ace73 100644 (file)
@@ -11,7 +11,7 @@ config XEN_BALLOON
 
 config XEN_SELFBALLOONING
        bool "Dynamically self-balloon kernel memory to target"
-       depends on XEN && XEN_BALLOON && CLEANCACHE && SWAP
+       depends on XEN && XEN_BALLOON && CLEANCACHE && SWAP && XEN_TMEM
        default n
        help
          Self-ballooning dynamically balloons available kernel memory driven
index e9cb57f07546b4a5f0d0ba5074c0f63276d4bc6d..9a1d426307510135444da722883e570fe52410b9 100644 (file)
@@ -182,11 +182,11 @@ int v9fs_set_create_acl(struct dentry *dentry,
        return 0;
 }
 
-int v9fs_acl_mode(struct inode *dir, mode_t *modep,
+int v9fs_acl_mode(struct inode *dir, umode_t *modep,
                  struct posix_acl **dpacl, struct posix_acl **pacl)
 {
        int retval = 0;
-       mode_t mode = *modep;
+       umode_t mode = *modep;
        struct posix_acl *acl = NULL;
 
        if (!S_ISLNK(mode)) {
@@ -319,7 +319,7 @@ static int v9fs_xattr_set_acl(struct dentry *dentry, const char *name,
        case ACL_TYPE_ACCESS:
                name = POSIX_ACL_XATTR_ACCESS;
                if (acl) {
-                       mode_t mode = inode->i_mode;
+                       umode_t mode = inode->i_mode;
                        retval = posix_acl_equiv_mode(acl, &mode);
                        if (retval < 0)
                                goto err_out;
index ddb7ae19d9718933398fad6d18ac11bcc15c116c..55955641196593afe5d01327c0c14bcbf7ef31a0 100644 (file)
@@ -20,7 +20,7 @@ extern struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type);
 extern int v9fs_acl_chmod(struct dentry *);
 extern int v9fs_set_create_acl(struct dentry *,
                               struct posix_acl **, struct posix_acl **);
-extern int v9fs_acl_mode(struct inode *dir, mode_t *modep,
+extern int v9fs_acl_mode(struct inode *dir, umode_t *modep,
                         struct posix_acl **dpacl, struct posix_acl **pacl);
 #else
 #define v9fs_iop_get_acl NULL
@@ -38,7 +38,7 @@ static inline int v9fs_set_create_acl(struct dentry *dentry,
 {
        return 0;
 }
-static inline int v9fs_acl_mode(struct inode *dir, mode_t *modep,
+static inline int v9fs_acl_mode(struct inode *dir, umode_t *modep,
                                struct posix_acl **dpacl,
                                struct posix_acl **pacl)
 {
index 9a26dce5a99f023e9d2d53b95dd8fb5286cf30b0..b6c8ed205192e5ab34f1b8ae15323dcc01fdec34 100644 (file)
@@ -206,7 +206,7 @@ v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int omode,
        int err = 0;
        gid_t gid;
        int flags;
-       mode_t mode;
+       umode_t mode;
        char *name = NULL;
        struct file *filp;
        struct p9_qid qid;
@@ -348,7 +348,7 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir,
        struct p9_fid *fid = NULL, *dfid = NULL;
        gid_t gid;
        char *name;
-       mode_t mode;
+       umode_t mode;
        struct inode *inode;
        struct p9_qid qid;
        struct dentry *dir_dentry;
@@ -751,7 +751,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode,
        int err;
        gid_t gid;
        char *name;
-       mode_t mode;
+       umode_t mode;
        struct v9fs_session_info *v9ses;
        struct p9_fid *fid = NULL, *dfid = NULL;
        struct inode *inode;
index 19891aab9c6ed7255388f175eabfcd7e4f5bb253..9fe0b349f4cd9e58c6a4bef810239f2f149e0b39 100644 (file)
@@ -127,14 +127,21 @@ config TMPFS_POSIX_ACL
        select TMPFS_XATTR
        select GENERIC_ACL
        help
-         POSIX Access Control Lists (ACLs) support permissions for users and
-         groups beyond the owner/group/world scheme.
+         POSIX Access Control Lists (ACLs) support additional access rights
+         for users and groups beyond the standard owner/group/world scheme,
+         and this option selects support for ACLs specifically for tmpfs
+         filesystems.
+
+         If you've selected TMPFS, it's possible that you'll also need
+         this option as there are a number of Linux distros that require
+         POSIX ACL support under /dev for certain features to work properly.
+         For example, some distros need this feature for ALSA-related /dev
+         files for sound to work properly.  In short, if you're not sure,
+         say Y.
 
          To learn more about Access Control Lists, visit the POSIX ACLs for
          Linux website <http://acl.bestbits.at/>.
 
-         If you don't know what Access Control Lists are, say N.
-
 config TMPFS_XATTR
        bool "Tmpfs extended attributes"
        depends on TMPFS
index f55aad4d16110fa8a70d5c0f99b87687eabccd90..ff77262e887cb66819f31d781e679eccb2d1975a 100644 (file)
@@ -387,6 +387,10 @@ int blkdev_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
        struct inode *bd_inode = filp->f_mapping->host;
        struct block_device *bdev = I_BDEV(bd_inode);
        int error;
+       
+       error = filemap_write_and_wait_range(filp->f_mapping, start, end);
+       if (error)
+               return error;
 
        /*
         * There is no need to serialise calls to blkdev_issue_flush with
@@ -552,6 +556,7 @@ struct block_device *bdget(dev_t dev)
 
        if (inode->i_state & I_NEW) {
                bdev->bd_contains = NULL;
+               bdev->bd_super = NULL;
                bdev->bd_inode = inode;
                bdev->bd_block_size = (1 << inode->i_blkbits);
                bdev->bd_part_count = 0;
index 9b72dcf1cd258bd2e7733079b399ddd6413e2826..40e6ac08c21ffb3f5603c559de6d2fcf5f56d8f4 100644 (file)
@@ -6,5 +6,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
           transaction.o inode.o file.o tree-defrag.o \
           extent_map.o sysfs.o struct-funcs.o xattr.o ordered-data.o \
           extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \
-          export.o tree-log.o acl.o free-space-cache.o zlib.o lzo.o \
+          export.o tree-log.o free-space-cache.o zlib.o lzo.o \
           compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o
+
+btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
index 65a735d8f6e44443c6044d6b71a59e5acf91f546..eb159aaa5a1139204e2d24333bc32eae8433261d 100644 (file)
@@ -28,8 +28,6 @@
 #include "btrfs_inode.h"
 #include "xattr.h"
 
-#ifdef CONFIG_BTRFS_FS_POSIX_ACL
-
 struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
 {
        int size;
@@ -111,7 +109,6 @@ static int btrfs_set_acl(struct btrfs_trans_handle *trans,
        int ret, size = 0;
        const char *name;
        char *value = NULL;
-       mode_t mode;
 
        if (acl) {
                ret = posix_acl_valid(acl);
@@ -122,13 +119,11 @@ static int btrfs_set_acl(struct btrfs_trans_handle *trans,
 
        switch (type) {
        case ACL_TYPE_ACCESS:
-               mode = inode->i_mode;
                name = POSIX_ACL_XATTR_ACCESS;
                if (acl) {
-                       ret = posix_acl_equiv_mode(acl, &mode);
+                       ret = posix_acl_equiv_mode(acl, &inode->i_mode);
                        if (ret < 0)
                                return ret;
-                       inode->i_mode = mode;
                }
                ret = 0;
                break;
@@ -222,19 +217,16 @@ int btrfs_init_acl(struct btrfs_trans_handle *trans,
        }
 
        if (IS_POSIXACL(dir) && acl) {
-               mode_t mode = inode->i_mode;
-
                if (S_ISDIR(inode->i_mode)) {
                        ret = btrfs_set_acl(trans, inode, acl,
                                            ACL_TYPE_DEFAULT);
                        if (ret)
                                goto failed;
                }
-               ret = posix_acl_create(&acl, GFP_NOFS, &mode);
+               ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
                if (ret < 0)
                        return ret;
 
-               inode->i_mode = mode;
                if (ret > 0) {
                        /* we need an acl */
                        ret = btrfs_set_acl(trans, inode, acl, ACL_TYPE_ACCESS);
@@ -282,18 +274,3 @@ const struct xattr_handler btrfs_xattr_acl_access_handler = {
        .get    = btrfs_xattr_acl_get,
        .set    = btrfs_xattr_acl_set,
 };
-
-#else /* CONFIG_BTRFS_FS_POSIX_ACL */
-
-int btrfs_acl_chmod(struct inode *inode)
-{
-       return 0;
-}
-
-int btrfs_init_acl(struct btrfs_trans_handle *trans,
-                  struct inode *inode, struct inode *dir)
-{
-       return 0;
-}
-
-#endif /* CONFIG_BTRFS_FS_POSIX_ACL */
index bfe42b03eaf9b3cc0bdf14b3975300e9a30b8584..8ec5d86f173471a7c51177ae7473fb494dcc8f29 100644 (file)
@@ -338,6 +338,7 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start,
        u64 first_byte = disk_start;
        struct block_device *bdev;
        int ret;
+       int skip_sum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;
 
        WARN_ON(start & ((u64)PAGE_CACHE_SIZE - 1));
        cb = kmalloc(compressed_bio_size(root, compressed_len), GFP_NOFS);
@@ -392,8 +393,11 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start,
                        ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
                        BUG_ON(ret);
 
-                       ret = btrfs_csum_one_bio(root, inode, bio, start, 1);
-                       BUG_ON(ret);
+                       if (!skip_sum) {
+                               ret = btrfs_csum_one_bio(root, inode, bio,
+                                                        start, 1);
+                               BUG_ON(ret);
+                       }
 
                        ret = btrfs_map_bio(root, WRITE, bio, 0, 1);
                        BUG_ON(ret);
@@ -418,8 +422,10 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start,
        ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);
        BUG_ON(ret);
 
-       ret = btrfs_csum_one_bio(root, inode, bio, start, 1);
-       BUG_ON(ret);
+       if (!skip_sum) {
+               ret = btrfs_csum_one_bio(root, inode, bio, start, 1);
+               BUG_ON(ret);
+       }
 
        ret = btrfs_map_bio(root, WRITE, bio, 0, 1);
        BUG_ON(ret);
index 365c4e1dde04968379b86568dbb129bd6f3f0dbc..0469263e327eebd66f36b6ba9a4e8a1ba67850d5 100644 (file)
@@ -2406,8 +2406,8 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct
                         btrfs_root_item *item, struct btrfs_key *key);
 int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid);
 int btrfs_find_orphan_roots(struct btrfs_root *tree_root);
-int btrfs_set_root_node(struct btrfs_root_item *item,
-                       struct extent_buffer *node);
+void btrfs_set_root_node(struct btrfs_root_item *item,
+                        struct extent_buffer *node);
 void btrfs_check_and_init_root_item(struct btrfs_root_item *item);
 
 /* dir-item.c */
@@ -2523,6 +2523,14 @@ struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *pag
 #define PageChecked PageFsMisc
 #endif
 
+/* This forces readahead on a given range of bytes in an inode */
+static inline void btrfs_force_ra(struct address_space *mapping,
+                                 struct file_ra_state *ra, struct file *file,
+                                 pgoff_t offset, unsigned long req_size)
+{
+       page_cache_sync_readahead(mapping, ra, file, offset, req_size);
+}
+
 struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry);
 int btrfs_set_inode_index(struct inode *dir, u64 *index);
 int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
@@ -2551,9 +2559,6 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
 int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
                         size_t size, struct bio *bio, unsigned long bio_flags);
 
-unsigned long btrfs_force_ra(struct address_space *mapping,
-                             struct file_ra_state *ra, struct file *file,
-                             pgoff_t offset, pgoff_t last_index);
 int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
 int btrfs_readpage(struct file *file, struct page *page);
 void btrfs_evict_inode(struct inode *inode);
@@ -2648,12 +2653,21 @@ do {                                                            \
 /* acl.c */
 #ifdef CONFIG_BTRFS_FS_POSIX_ACL
 struct posix_acl *btrfs_get_acl(struct inode *inode, int type);
-#else
-#define btrfs_get_acl NULL
-#endif
 int btrfs_init_acl(struct btrfs_trans_handle *trans,
                   struct inode *inode, struct inode *dir);
 int btrfs_acl_chmod(struct inode *inode);
+#else
+#define btrfs_get_acl NULL
+static inline int btrfs_init_acl(struct btrfs_trans_handle *trans,
+                                struct inode *inode, struct inode *dir)
+{
+       return 0;
+}
+static inline int btrfs_acl_chmod(struct inode *inode)
+{
+       return 0;
+}
+#endif
 
 /* relocation.c */
 int btrfs_relocate_block_group(struct btrfs_root *root, u64 group_start);
index c360a848d97fb6115c58a41bb45aeef611c4c86f..31d84e78129b34adaac7a1ec25368e8c3212403d 100644 (file)
@@ -198,8 +198,6 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
        struct btrfs_key key;
        int ins_len = mod < 0 ? -1 : 0;
        int cow = mod != 0;
-       struct btrfs_key found_key;
-       struct extent_buffer *leaf;
 
        key.objectid = dir;
        btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
@@ -209,18 +207,7 @@ struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
        ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
        if (ret < 0)
                return ERR_PTR(ret);
-       if (ret > 0) {
-               if (path->slots[0] == 0)
-                       return NULL;
-               path->slots[0]--;
-       }
-
-       leaf = path->nodes[0];
-       btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-
-       if (found_key.objectid != dir ||
-           btrfs_key_type(&found_key) != BTRFS_DIR_ITEM_KEY ||
-           found_key.offset != key.offset)
+       if (ret > 0)
                return NULL;
 
        return btrfs_match_dir_item_name(root, path, name, name_len);
@@ -315,8 +302,6 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans,
        struct btrfs_key key;
        int ins_len = mod < 0 ? -1 : 0;
        int cow = mod != 0;
-       struct btrfs_key found_key;
-       struct extent_buffer *leaf;
 
        key.objectid = dir;
        btrfs_set_key_type(&key, BTRFS_XATTR_ITEM_KEY);
@@ -324,18 +309,7 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans,
        ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
        if (ret < 0)
                return ERR_PTR(ret);
-       if (ret > 0) {
-               if (path->slots[0] == 0)
-                       return NULL;
-               path->slots[0]--;
-       }
-
-       leaf = path->nodes[0];
-       btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
-
-       if (found_key.objectid != dir ||
-           btrfs_key_type(&found_key) != BTRFS_XATTR_ITEM_KEY ||
-           found_key.offset != key.offset)
+       if (ret > 0)
                return NULL;
 
        return btrfs_match_dir_item_name(root, path, name, name_len);
index 4d08ed79405d2c5729fd78d45c75a02f4e556687..66bac226944e05ae82edb56a9fd43116f31a901a 100644 (file)
@@ -663,7 +663,9 @@ int btrfs_lookup_extent(struct btrfs_root *root, u64 start, u64 len)
        struct btrfs_path *path;
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return -ENOMEM;
+
        key.objectid = start;
        key.offset = len;
        btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
@@ -3272,6 +3274,9 @@ again:
        }
 
        ret = btrfs_alloc_chunk(trans, extent_root, flags);
+       if (ret < 0 && ret != -ENOSPC)
+               goto out;
+
        spin_lock(&space_info->lock);
        if (ret)
                space_info->full = 1;
@@ -3281,6 +3286,7 @@ again:
        space_info->force_alloc = CHUNK_ALLOC_NO_FORCE;
        space_info->chunk_alloc = 0;
        spin_unlock(&space_info->lock);
+out:
        mutex_unlock(&extent_root->fs_info->chunk_mutex);
        return ret;
 }
@@ -4456,7 +4462,9 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
                                printk(KERN_ERR "umm, got %d back from search"
                                       ", was looking for %llu\n", ret,
                                       (unsigned long long)bytenr);
-                               btrfs_print_leaf(extent_root, path->nodes[0]);
+                               if (ret > 0)
+                                       btrfs_print_leaf(extent_root,
+                                                        path->nodes[0]);
                        }
                        BUG_ON(ret);
                        extent_slot = path->slots[0];
@@ -5073,7 +5081,9 @@ have_block_group:
                         * group is does point to and try again
                         */
                        if (!last_ptr_loop && last_ptr->block_group &&
-                           last_ptr->block_group != block_group) {
+                           last_ptr->block_group != block_group &&
+                           index <=
+                                get_block_group_index(last_ptr->block_group)) {
 
                                btrfs_put_block_group(block_group);
                                block_group = last_ptr->block_group;
@@ -5501,7 +5511,8 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
        u32 size = sizeof(*extent_item) + sizeof(*block_info) + sizeof(*iref);
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return -ENOMEM;
 
        path->leave_spinning = 1;
        ret = btrfs_insert_empty_item(trans, fs_info->extent_root, path,
@@ -6272,10 +6283,14 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
        int level;
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return -ENOMEM;
 
        wc = kzalloc(sizeof(*wc), GFP_NOFS);
-       BUG_ON(!wc);
+       if (!wc) {
+               btrfs_free_path(path);
+               return -ENOMEM;
+       }
 
        trans = btrfs_start_transaction(tree_root, 0);
        BUG_ON(IS_ERR(trans));
@@ -6538,8 +6553,6 @@ static int set_block_group_ro(struct btrfs_block_group_cache *cache, int force)
        u64 min_allocable_bytes;
        int ret = -ENOSPC;
 
-       if (cache->ro)
-               return 0;
 
        /*
         * We need some metadata space and system metadata space for
@@ -6555,6 +6568,12 @@ static int set_block_group_ro(struct btrfs_block_group_cache *cache, int force)
 
        spin_lock(&sinfo->lock);
        spin_lock(&cache->lock);
+
+       if (cache->ro) {
+               ret = 0;
+               goto out;
+       }
+
        num_bytes = cache->key.offset - cache->reserved - cache->pinned -
                    cache->bytes_super - btrfs_block_group_used(&cache->item);
 
@@ -6568,7 +6587,7 @@ static int set_block_group_ro(struct btrfs_block_group_cache *cache, int force)
                cache->ro = 1;
                ret = 0;
        }
-
+out:
        spin_unlock(&cache->lock);
        spin_unlock(&sinfo->lock);
        return ret;
@@ -7183,11 +7202,15 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
        spin_unlock(&cluster->refill_lock);
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
        inode = lookup_free_space_inode(root, block_group, path);
        if (!IS_ERR(inode)) {
-               btrfs_orphan_add(trans, inode);
+               ret = btrfs_orphan_add(trans, inode);
+               BUG_ON(ret);
                clear_nlink(inode);
                /* One for the block groups ref */
                spin_lock(&block_group->lock);
index 067b1747421bfd655b2d0ddbd228f195cca65553..d418164a35f134842ed3af4cfd5f43666b37c89d 100644 (file)
@@ -254,14 +254,14 @@ static void merge_cb(struct extent_io_tree *tree, struct extent_state *new,
  *
  * This should be called with the tree lock held.
  */
-static int merge_state(struct extent_io_tree *tree,
-                      struct extent_state *state)
+static void merge_state(struct extent_io_tree *tree,
+                       struct extent_state *state)
 {
        struct extent_state *other;
        struct rb_node *other_node;
 
        if (state->state & (EXTENT_IOBITS | EXTENT_BOUNDARY))
-               return 0;
+               return;
 
        other_node = rb_prev(&state->rb_node);
        if (other_node) {
@@ -287,19 +287,13 @@ static int merge_state(struct extent_io_tree *tree,
                        free_extent_state(other);
                }
        }
-
-       return 0;
 }
 
-static int set_state_cb(struct extent_io_tree *tree,
+static void set_state_cb(struct extent_io_tree *tree,
                         struct extent_state *state, int *bits)
 {
-       if (tree->ops && tree->ops->set_bit_hook) {
-               return tree->ops->set_bit_hook(tree->mapping->host,
-                                              state, bits);
-       }
-
-       return 0;
+       if (tree->ops && tree->ops->set_bit_hook)
+               tree->ops->set_bit_hook(tree->mapping->host, state, bits);
 }
 
 static void clear_state_cb(struct extent_io_tree *tree,
@@ -309,6 +303,9 @@ static void clear_state_cb(struct extent_io_tree *tree,
                tree->ops->clear_bit_hook(tree->mapping->host, state, bits);
 }
 
+static void set_state_bits(struct extent_io_tree *tree,
+                          struct extent_state *state, int *bits);
+
 /*
  * insert an extent_state struct into the tree.  'bits' are set on the
  * struct before it is inserted.
@@ -324,8 +321,6 @@ static int insert_state(struct extent_io_tree *tree,
                        int *bits)
 {
        struct rb_node *node;
-       int bits_to_set = *bits & ~EXTENT_CTLBITS;
-       int ret;
 
        if (end < start) {
                printk(KERN_ERR "btrfs end < start %llu %llu\n",
@@ -335,13 +330,9 @@ static int insert_state(struct extent_io_tree *tree,
        }
        state->start = start;
        state->end = end;
-       ret = set_state_cb(tree, state, bits);
-       if (ret)
-               return ret;
 
-       if (bits_to_set & EXTENT_DIRTY)
-               tree->dirty_bytes += end - start + 1;
-       state->state |= bits_to_set;
+       set_state_bits(tree, state, bits);
+
        node = tree_insert(&tree->state, end, &state->rb_node);
        if (node) {
                struct extent_state *found;
@@ -357,13 +348,11 @@ static int insert_state(struct extent_io_tree *tree,
        return 0;
 }
 
-static int split_cb(struct extent_io_tree *tree, struct extent_state *orig,
+static void split_cb(struct extent_io_tree *tree, struct extent_state *orig,
                     u64 split)
 {
        if (tree->ops && tree->ops->split_extent_hook)
-               return tree->ops->split_extent_hook(tree->mapping->host,
-                                                   orig, split);
-       return 0;
+               tree->ops->split_extent_hook(tree->mapping->host, orig, split);
 }
 
 /*
@@ -659,34 +648,25 @@ again:
                if (start > end)
                        break;
 
-               if (need_resched()) {
-                       spin_unlock(&tree->lock);
-                       cond_resched();
-                       spin_lock(&tree->lock);
-               }
+               cond_resched_lock(&tree->lock);
        }
 out:
        spin_unlock(&tree->lock);
        return 0;
 }
 
-static int set_state_bits(struct extent_io_tree *tree,
+static void set_state_bits(struct extent_io_tree *tree,
                           struct extent_state *state,
                           int *bits)
 {
-       int ret;
        int bits_to_set = *bits & ~EXTENT_CTLBITS;
 
-       ret = set_state_cb(tree, state, bits);
-       if (ret)
-               return ret;
+       set_state_cb(tree, state, bits);
        if ((bits_to_set & EXTENT_DIRTY) && !(state->state & EXTENT_DIRTY)) {
                u64 range = state->end - state->start + 1;
                tree->dirty_bytes += range;
        }
        state->state |= bits_to_set;
-
-       return 0;
 }
 
 static void cache_state(struct extent_state *state,
@@ -779,9 +759,7 @@ hit_next:
                        goto out;
                }
 
-               err = set_state_bits(tree, state, &bits);
-               if (err)
-                       goto out;
+               set_state_bits(tree, state, &bits);
 
                cache_state(state, cached_state);
                merge_state(tree, state);
@@ -830,9 +808,7 @@ hit_next:
                if (err)
                        goto out;
                if (state->end <= end) {
-                       err = set_state_bits(tree, state, &bits);
-                       if (err)
-                               goto out;
+                       set_state_bits(tree, state, &bits);
                        cache_state(state, cached_state);
                        merge_state(tree, state);
                        if (last_end == (u64)-1)
@@ -893,11 +869,7 @@ hit_next:
                err = split_state(tree, state, prealloc, end + 1);
                BUG_ON(err == -EEXIST);
 
-               err = set_state_bits(tree, prealloc, &bits);
-               if (err) {
-                       prealloc = NULL;
-                       goto out;
-               }
+               set_state_bits(tree, prealloc, &bits);
                cache_state(prealloc, cached_state);
                merge_state(tree, prealloc);
                prealloc = NULL;
@@ -1059,46 +1031,6 @@ static int set_range_writeback(struct extent_io_tree *tree, u64 start, u64 end)
        return 0;
 }
 
-/*
- * find the first offset in the io tree with 'bits' set. zero is
- * returned if we find something, and *start_ret and *end_ret are
- * set to reflect the state struct that was found.
- *
- * If nothing was found, 1 is returned, < 0 on error
- */
-int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
-                         u64 *start_ret, u64 *end_ret, int bits)
-{
-       struct rb_node *node;
-       struct extent_state *state;
-       int ret = 1;
-
-       spin_lock(&tree->lock);
-       /*
-        * this search will find all the extents that end after
-        * our range starts.
-        */
-       node = tree_search(tree, start);
-       if (!node)
-               goto out;
-
-       while (1) {
-               state = rb_entry(node, struct extent_state, rb_node);
-               if (state->end >= start && (state->state & bits)) {
-                       *start_ret = state->start;
-                       *end_ret = state->end;
-                       ret = 0;
-                       break;
-               }
-               node = rb_next(node);
-               if (!node)
-                       break;
-       }
-out:
-       spin_unlock(&tree->lock);
-       return ret;
-}
-
 /* find the first state struct with 'bits' set after 'start', and
  * return it.  tree->lock must be held.  NULL will returned if
  * nothing was found after 'start'
@@ -1130,6 +1062,30 @@ out:
        return NULL;
 }
 
+/*
+ * find the first offset in the io tree with 'bits' set. zero is
+ * returned if we find something, and *start_ret and *end_ret are
+ * set to reflect the state struct that was found.
+ *
+ * If nothing was found, 1 is returned, < 0 on error
+ */
+int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
+                         u64 *start_ret, u64 *end_ret, int bits)
+{
+       struct extent_state *state;
+       int ret = 1;
+
+       spin_lock(&tree->lock);
+       state = find_first_extent_bit_state(tree, start, bits);
+       if (state) {
+               *start_ret = state->start;
+               *end_ret = state->end;
+               ret = 0;
+       }
+       spin_unlock(&tree->lock);
+       return ret;
+}
+
 /*
  * find a contiguous range of bytes in the file marked as delalloc, not
  * more than 'max_bytes'.  start and end are used to return the range,
@@ -2546,7 +2502,6 @@ int extent_write_full_page(struct extent_io_tree *tree, struct page *page,
                          struct writeback_control *wbc)
 {
        int ret;
-       struct address_space *mapping = page->mapping;
        struct extent_page_data epd = {
                .bio = NULL,
                .tree = tree,
@@ -2554,17 +2509,9 @@ int extent_write_full_page(struct extent_io_tree *tree, struct page *page,
                .extent_locked = 0,
                .sync_io = wbc->sync_mode == WB_SYNC_ALL,
        };
-       struct writeback_control wbc_writepages = {
-               .sync_mode      = wbc->sync_mode,
-               .nr_to_write    = 64,
-               .range_start    = page_offset(page) + PAGE_CACHE_SIZE,
-               .range_end      = (loff_t)-1,
-       };
 
        ret = __extent_writepage(page, wbc, &epd);
 
-       extent_write_cache_pages(tree, mapping, &wbc_writepages,
-                                __extent_writepage, &epd, flush_write_bio);
        flush_epd_write_bio(&epd);
        return ret;
 }
index 21a7ca9e72825752a3b910cdfb9fac444094b96b..7b2f0c3e79292a2f73aec86ed112e46b1a438c69 100644 (file)
@@ -76,15 +76,15 @@ struct extent_io_ops {
                                    struct extent_state *state);
        int (*writepage_end_io_hook)(struct page *page, u64 start, u64 end,
                                      struct extent_state *state, int uptodate);
-       int (*set_bit_hook)(struct inode *inode, struct extent_state *state,
-                           int *bits);
-       int (*clear_bit_hook)(struct inode *inode, struct extent_state *state,
-                             int *bits);
-       int (*merge_extent_hook)(struct inode *inode,
-                                struct extent_state *new,
-                                struct extent_state *other);
-       int (*split_extent_hook)(struct inode *inode,
-                                struct extent_state *orig, u64 split);
+       void (*set_bit_hook)(struct inode *inode, struct extent_state *state,
+                            int *bits);
+       void (*clear_bit_hook)(struct inode *inode, struct extent_state *state,
+                              int *bits);
+       void (*merge_extent_hook)(struct inode *inode,
+                                 struct extent_state *new,
+                                 struct extent_state *other);
+       void (*split_extent_hook)(struct inode *inode,
+                                 struct extent_state *orig, u64 split);
        int (*write_cache_pages_lock_hook)(struct page *page);
 };
 
@@ -108,8 +108,6 @@ struct extent_state {
        wait_queue_head_t wq;
        atomic_t refs;
        unsigned long state;
-       u64 split_start;
-       u64 split_end;
 
        /* for use by the FS */
        u64 private;
index 2d0410344ea3667a7d505b2c543e7ba212dd01ae..7c97b330145981d241cec9f13a75b3fa1869f8db 100644 (file)
@@ -183,22 +183,10 @@ static int mergable_maps(struct extent_map *prev, struct extent_map *next)
        return 0;
 }
 
-int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len)
+static void try_merge_map(struct extent_map_tree *tree, struct extent_map *em)
 {
-       int ret = 0;
        struct extent_map *merge = NULL;
        struct rb_node *rb;
-       struct extent_map *em;
-
-       write_lock(&tree->lock);
-       em = lookup_extent_mapping(tree, start, len);
-
-       WARN_ON(!em || em->start != start);
-
-       if (!em)
-               goto out;
-
-       clear_bit(EXTENT_FLAG_PINNED, &em->flags);
 
        if (em->start != 0) {
                rb = rb_prev(&em->rb_node);
@@ -225,6 +213,24 @@ int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len)
                merge->in_tree = 0;
                free_extent_map(merge);
        }
+}
+
+int unpin_extent_cache(struct extent_map_tree *tree, u64 start, u64 len)
+{
+       int ret = 0;
+       struct extent_map *em;
+
+       write_lock(&tree->lock);
+       em = lookup_extent_mapping(tree, start, len);
+
+       WARN_ON(!em || em->start != start);
+
+       if (!em)
+               goto out;
+
+       clear_bit(EXTENT_FLAG_PINNED, &em->flags);
+
+       try_merge_map(tree, em);
 
        free_extent_map(em);
 out:
@@ -247,7 +253,6 @@ int add_extent_mapping(struct extent_map_tree *tree,
                       struct extent_map *em)
 {
        int ret = 0;
-       struct extent_map *merge = NULL;
        struct rb_node *rb;
        struct extent_map *exist;
 
@@ -263,30 +268,8 @@ int add_extent_mapping(struct extent_map_tree *tree,
                goto out;
        }
        atomic_inc(&em->refs);
-       if (em->start != 0) {
-               rb = rb_prev(&em->rb_node);
-               if (rb)
-                       merge = rb_entry(rb, struct extent_map, rb_node);
-               if (rb && mergable_maps(merge, em)) {
-                       em->start = merge->start;
-                       em->len += merge->len;
-                       em->block_len += merge->block_len;
-                       em->block_start = merge->block_start;
-                       merge->in_tree = 0;
-                       rb_erase(&merge->rb_node, &tree->map);
-                       free_extent_map(merge);
-               }
-        }
-       rb = rb_next(&em->rb_node);
-       if (rb)
-               merge = rb_entry(rb, struct extent_map, rb_node);
-       if (rb && mergable_maps(em, merge)) {
-               em->len += merge->len;
-               em->block_len += merge->len;
-               rb_erase(&merge->rb_node, &tree->map);
-               merge->in_tree = 0;
-               free_extent_map(merge);
-       }
+
+       try_merge_map(tree, em);
 out:
        return ret;
 }
@@ -299,19 +282,8 @@ static u64 range_end(u64 start, u64 len)
        return start + len;
 }
 
-/**
- * lookup_extent_mapping - lookup extent_map
- * @tree:      tree to lookup in
- * @start:     byte offset to start the search
- * @len:       length of the lookup range
- *
- * Find and return the first extent_map struct in @tree that intersects the
- * [start, len] range.  There may be additional objects in the tree that
- * intersect, so check the object returned carefully to make sure that no
- * additional lookups are needed.
- */
-struct extent_map *lookup_extent_mapping(struct extent_map_tree *tree,
-                                        u64 start, u64 len)
+struct extent_map *__lookup_extent_mapping(struct extent_map_tree *tree,
+                                          u64 start, u64 len, int strict)
 {
        struct extent_map *em;
        struct rb_node *rb_node;
@@ -320,37 +292,41 @@ struct extent_map *lookup_extent_mapping(struct extent_map_tree *tree,
        u64 end = range_end(start, len);
 
        rb_node = __tree_search(&tree->map, start, &prev, &next);
-       if (!rb_node && prev) {
-               em = rb_entry(prev, struct extent_map, rb_node);
-               if (end > em->start && start < extent_map_end(em))
-                       goto found;
-       }
-       if (!rb_node && next) {
-               em = rb_entry(next, struct extent_map, rb_node);
-               if (end > em->start && start < extent_map_end(em))
-                       goto found;
-       }
        if (!rb_node) {
-               em = NULL;
-               goto out;
-       }
-       if (IS_ERR(rb_node)) {
-               em = ERR_CAST(rb_node);
-               goto out;
+               if (prev)
+                       rb_node = prev;
+               else if (next)
+                       rb_node = next;
+               else
+                       return NULL;
        }
+
        em = rb_entry(rb_node, struct extent_map, rb_node);
-       if (end > em->start && start < extent_map_end(em))
-               goto found;
 
-       em = NULL;
-       goto out;
+       if (strict && !(end > em->start && start < extent_map_end(em)))
+               return NULL;
 
-found:
        atomic_inc(&em->refs);
-out:
        return em;
 }
 
+/**
+ * lookup_extent_mapping - lookup extent_map
+ * @tree:      tree to lookup in
+ * @start:     byte offset to start the search
+ * @len:       length of the lookup range
+ *
+ * Find and return the first extent_map struct in @tree that intersects the
+ * [start, len] range.  There may be additional objects in the tree that
+ * intersect, so check the object returned carefully to make sure that no
+ * additional lookups are needed.
+ */
+struct extent_map *lookup_extent_mapping(struct extent_map_tree *tree,
+                                        u64 start, u64 len)
+{
+       return __lookup_extent_mapping(tree, start, len, 1);
+}
+
 /**
  * search_extent_mapping - find a nearby extent map
  * @tree:      tree to lookup in
@@ -365,38 +341,7 @@ out:
 struct extent_map *search_extent_mapping(struct extent_map_tree *tree,
                                         u64 start, u64 len)
 {
-       struct extent_map *em;
-       struct rb_node *rb_node;
-       struct rb_node *prev = NULL;
-       struct rb_node *next = NULL;
-
-       rb_node = __tree_search(&tree->map, start, &prev, &next);
-       if (!rb_node && prev) {
-               em = rb_entry(prev, struct extent_map, rb_node);
-               goto found;
-       }
-       if (!rb_node && next) {
-               em = rb_entry(next, struct extent_map, rb_node);
-               goto found;
-       }
-       if (!rb_node) {
-               em = NULL;
-               goto out;
-       }
-       if (IS_ERR(rb_node)) {
-               em = ERR_CAST(rb_node);
-               goto out;
-       }
-       em = rb_entry(rb_node, struct extent_map, rb_node);
-       goto found;
-
-       em = NULL;
-       goto out;
-
-found:
-       atomic_inc(&em->refs);
-out:
-       return em;
+       return __lookup_extent_mapping(tree, start, len, 0);
 }
 
 /**
index 08bcfa92a2226bcfd3ba0edf6fe8eb944454612b..b910694f61ed25bbf0f309ff673381afcf0994d8 100644 (file)
@@ -291,7 +291,8 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
        u16 csum_size = btrfs_super_csum_size(&root->fs_info->super_copy);
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return -ENOMEM;
 
        if (search_commit) {
                path->skip_locking = 1;
@@ -677,7 +678,9 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
                btrfs_super_csum_size(&root->fs_info->super_copy);
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return -ENOMEM;
+
        sector_sum = sums->sums;
 again:
        next_offset = (u64)-1;
index a35e51c9f235559c5c9a0f5f5ddea267e6c14a40..658d66959abe9eca7608f650d73bd9952a053ac4 100644 (file)
@@ -74,7 +74,7 @@ struct inode_defrag {
  * If an existing record is found the defrag item you
  * pass in is freed
  */
-static int __btrfs_add_inode_defrag(struct inode *inode,
+static void __btrfs_add_inode_defrag(struct inode *inode,
                                    struct inode_defrag *defrag)
 {
        struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -106,11 +106,11 @@ static int __btrfs_add_inode_defrag(struct inode *inode,
        BTRFS_I(inode)->in_defrag = 1;
        rb_link_node(&defrag->rb_node, parent, p);
        rb_insert_color(&defrag->rb_node, &root->fs_info->defrag_inodes);
-       return 0;
+       return;
 
 exists:
        kfree(defrag);
-       return 0;
+       return;
 
 }
 
@@ -123,7 +123,6 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
 {
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct inode_defrag *defrag;
-       int ret = 0;
        u64 transid;
 
        if (!btrfs_test_opt(root, AUTO_DEFRAG))
@@ -150,9 +149,9 @@ int btrfs_add_inode_defrag(struct btrfs_trans_handle *trans,
 
        spin_lock(&root->fs_info->defrag_inodes_lock);
        if (!BTRFS_I(inode)->in_defrag)
-               ret = __btrfs_add_inode_defrag(inode, defrag);
+               __btrfs_add_inode_defrag(inode, defrag);
        spin_unlock(&root->fs_info->defrag_inodes_lock);
-       return ret;
+       return 0;
 }
 
 /*
@@ -855,7 +854,8 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
        btrfs_drop_extent_cache(inode, start, end - 1, 0);
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return -ENOMEM;
 again:
        recow = 0;
        split = start;
@@ -1059,7 +1059,7 @@ static int prepare_uptodate_page(struct page *page, u64 pos)
 static noinline int prepare_pages(struct btrfs_root *root, struct file *file,
                         struct page **pages, size_t num_pages,
                         loff_t pos, unsigned long first_index,
-                        unsigned long last_index, size_t write_bytes)
+                        size_t write_bytes)
 {
        struct extent_state *cached_state = NULL;
        int i;
@@ -1159,7 +1159,6 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct page **pages = NULL;
        unsigned long first_index;
-       unsigned long last_index;
        size_t num_written = 0;
        int nrptrs;
        int ret = 0;
@@ -1172,7 +1171,6 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
                return -ENOMEM;
 
        first_index = pos >> PAGE_CACHE_SHIFT;
-       last_index = (pos + iov_iter_count(i)) >> PAGE_CACHE_SHIFT;
 
        while (iov_iter_count(i) > 0) {
                size_t offset = pos & (PAGE_CACHE_SIZE - 1);
@@ -1206,8 +1204,7 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file,
                 * contents of pages from loop to loop
                 */
                ret = prepare_pages(root, file, pages, num_pages,
-                                   pos, first_index, last_index,
-                                   write_bytes);
+                                   pos, first_index, write_bytes);
                if (ret) {
                        btrfs_delalloc_release_space(inode,
                                        num_pages << PAGE_CACHE_SHIFT);
index 13e6255182e3dad59af5463537dbc21abdff4bef..15fceefbca0a02b9d1c9ddf3a4da224228c2ec40 100644 (file)
@@ -1061,7 +1061,8 @@ static noinline int run_delalloc_nocow(struct inode *inode,
        u64 ino = btrfs_ino(inode);
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return -ENOMEM;
 
        nolock = btrfs_is_free_space_inode(root, inode);
 
@@ -1282,17 +1283,16 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,
        return ret;
 }
 
-static int btrfs_split_extent_hook(struct inode *inode,
-                                  struct extent_state *orig, u64 split)
+static void btrfs_split_extent_hook(struct inode *inode,
+                                   struct extent_state *orig, u64 split)
 {
        /* not delalloc, ignore it */
        if (!(orig->state & EXTENT_DELALLOC))
-               return 0;
+               return;
 
        spin_lock(&BTRFS_I(inode)->lock);
        BTRFS_I(inode)->outstanding_extents++;
        spin_unlock(&BTRFS_I(inode)->lock);
-       return 0;
 }
 
 /*
@@ -1301,18 +1301,17 @@ static int btrfs_split_extent_hook(struct inode *inode,
  * extents, such as when we are doing sequential writes, so we can properly
  * account for the metadata space we'll need.
  */
-static int btrfs_merge_extent_hook(struct inode *inode,
-                                  struct extent_state *new,
-                                  struct extent_state *other)
+static void btrfs_merge_extent_hook(struct inode *inode,
+                                   struct extent_state *new,
+                                   struct extent_state *other)
 {
        /* not delalloc, ignore it */
        if (!(other->state & EXTENT_DELALLOC))
-               return 0;
+               return;
 
        spin_lock(&BTRFS_I(inode)->lock);
        BTRFS_I(inode)->outstanding_extents--;
        spin_unlock(&BTRFS_I(inode)->lock);
-       return 0;
 }
 
 /*
@@ -1320,8 +1319,8 @@ static int btrfs_merge_extent_hook(struct inode *inode,
  * bytes in this file, and to maintain the list of inodes that
  * have pending delalloc work to be done.
  */
-static int btrfs_set_bit_hook(struct inode *inode,
-                             struct extent_state *state, int *bits)
+static void btrfs_set_bit_hook(struct inode *inode,
+                              struct extent_state *state, int *bits)
 {
 
        /*
@@ -1351,14 +1350,13 @@ static int btrfs_set_bit_hook(struct inode *inode,
                }
                spin_unlock(&root->fs_info->delalloc_lock);
        }
-       return 0;
 }
 
 /*
  * extent_io.c clear_bit_hook, see set_bit_hook for why
  */
-static int btrfs_clear_bit_hook(struct inode *inode,
-                               struct extent_state *state, int *bits)
+static void btrfs_clear_bit_hook(struct inode *inode,
+                                struct extent_state *state, int *bits)
 {
        /*
         * set_bit and clear bit hooks normally require _irqsave/restore
@@ -1395,7 +1393,6 @@ static int btrfs_clear_bit_hook(struct inode *inode,
                }
                spin_unlock(&root->fs_info->delalloc_lock);
        }
-       return 0;
 }
 
 /*
@@ -1645,7 +1642,8 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,
        int ret;
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return -ENOMEM;
 
        path->leave_spinning = 1;
 
@@ -2215,7 +2213,8 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode)
 
        if (!root->orphan_block_rsv) {
                block_rsv = btrfs_alloc_block_rsv(root);
-               BUG_ON(!block_rsv);
+               if (!block_rsv)
+                       return -ENOMEM;
        }
 
        spin_lock(&root->orphan_lock);
@@ -2517,7 +2516,9 @@ static void btrfs_read_locked_inode(struct inode *inode)
                filled = true;
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               goto make_bad;
+
        path->leave_spinning = 1;
        memcpy(&location, &BTRFS_I(inode)->location, sizeof(location));
 
@@ -2998,13 +2999,16 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
 
        ret = btrfs_unlink_inode(trans, root, dir, dentry->d_inode,
                                 dentry->d_name.name, dentry->d_name.len);
-       BUG_ON(ret);
+       if (ret)
+               goto out;
 
        if (inode->i_nlink == 0) {
                ret = btrfs_orphan_add(trans, inode);
-               BUG_ON(ret);
+               if (ret)
+                       goto out;
        }
 
+out:
        nr = trans->blocks_used;
        __unlink_end_trans(trans, root);
        btrfs_btree_balance_dirty(root, nr);
@@ -3147,6 +3151,11 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
 
        BUG_ON(new_size > 0 && min_type != BTRFS_EXTENT_DATA_KEY);
 
+       path = btrfs_alloc_path();
+       if (!path)
+               return -ENOMEM;
+       path->reada = -1;
+
        if (root->ref_cows || root == root->fs_info->tree_root)
                btrfs_drop_extent_cache(inode, new_size & (~mask), (u64)-1, 0);
 
@@ -3159,10 +3168,6 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
        if (min_type == 0 && root == BTRFS_I(inode)->root)
                btrfs_kill_delayed_inode_items(inode);
 
-       path = btrfs_alloc_path();
-       BUG_ON(!path);
-       path->reada = -1;
-
        key.objectid = ino;
        key.offset = (u64)-1;
        key.type = (u8)-1;
@@ -3690,7 +3695,8 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry,
        int ret = 0;
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return -ENOMEM;
 
        di = btrfs_lookup_dir_item(NULL, root, path, btrfs_ino(dir), name,
                                    namelen, 0);
@@ -3946,6 +3952,7 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
                         struct btrfs_root *root, int *new)
 {
        struct inode *inode;
+       int bad_inode = 0;
 
        inode = btrfs_iget_locked(s, location->objectid, root);
        if (!inode)
@@ -3955,10 +3962,19 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
                BTRFS_I(inode)->root = root;
                memcpy(&BTRFS_I(inode)->location, location, sizeof(*location));
                btrfs_read_locked_inode(inode);
-               inode_tree_add(inode);
-               unlock_new_inode(inode);
-               if (new)
-                       *new = 1;
+               if (!is_bad_inode(inode)) {
+                       inode_tree_add(inode);
+                       unlock_new_inode(inode);
+                       if (new)
+                               *new = 1;
+               } else {
+                       bad_inode = 1;
+               }
+       }
+
+       if (bad_inode) {
+               iput(inode);
+               inode = ERR_PTR(-ESTALE);
        }
 
        return inode;
@@ -3993,12 +4009,19 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
        struct btrfs_root *sub_root = root;
        struct btrfs_key location;
        int index;
-       int ret;
+       int ret = 0;
 
        if (dentry->d_name.len > BTRFS_NAME_LEN)
                return ERR_PTR(-ENAMETOOLONG);
 
-       ret = btrfs_inode_by_name(dir, dentry, &location);
+       if (unlikely(d_need_lookup(dentry))) {
+               memcpy(&location, dentry->d_fsdata, sizeof(struct btrfs_key));
+               kfree(dentry->d_fsdata);
+               dentry->d_fsdata = NULL;
+               d_clear_need_lookup(dentry);
+       } else {
+               ret = btrfs_inode_by_name(dir, dentry, &location);
+       }
 
        if (ret < 0)
                return ERR_PTR(ret);
@@ -4053,6 +4076,12 @@ static int btrfs_dentry_delete(const struct dentry *dentry)
        return 0;
 }
 
+static void btrfs_dentry_release(struct dentry *dentry)
+{
+       if (dentry->d_fsdata)
+               kfree(dentry->d_fsdata);
+}
+
 static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
                                   struct nameidata *nd)
 {
@@ -4075,6 +4104,7 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
        struct btrfs_path *path;
        struct list_head ins_list;
        struct list_head del_list;
+       struct qstr q;
        int ret;
        struct extent_buffer *leaf;
        int slot;
@@ -4164,6 +4194,7 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
 
                while (di_cur < di_total) {
                        struct btrfs_key location;
+                       struct dentry *tmp;
 
                        if (verify_dir_item(root, leaf, di))
                                break;
@@ -4184,6 +4215,33 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
                        d_type = btrfs_filetype_table[btrfs_dir_type(leaf, di)];
                        btrfs_dir_item_key_to_cpu(leaf, di, &location);
 
+                       q.name = name_ptr;
+                       q.len = name_len;
+                       q.hash = full_name_hash(q.name, q.len);
+                       tmp = d_lookup(filp->f_dentry, &q);
+                       if (!tmp) {
+                               struct btrfs_key *newkey;
+
+                               newkey = kzalloc(sizeof(struct btrfs_key),
+                                                GFP_NOFS);
+                               if (!newkey)
+                                       goto no_dentry;
+                               tmp = d_alloc(filp->f_dentry, &q);
+                               if (!tmp) {
+                                       kfree(newkey);
+                                       dput(tmp);
+                                       goto no_dentry;
+                               }
+                               memcpy(newkey, &location,
+                                      sizeof(struct btrfs_key));
+                               tmp->d_fsdata = newkey;
+                               tmp->d_flags |= DCACHE_NEED_LOOKUP;
+                               d_rehash(tmp);
+                               dput(tmp);
+                       } else {
+                               dput(tmp);
+                       }
+no_dentry:
                        /* is this a reference to our own snapshot? If so
                         * skip it
                         */
@@ -4409,7 +4467,8 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
        int owner;
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return ERR_PTR(-ENOMEM);
 
        inode = new_inode(root->fs_info->sb);
        if (!inode) {
@@ -6669,19 +6728,6 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
        return 0;
 }
 
-/* helper function for file defrag and space balancing.  This
- * forces readahead on a given range of bytes in an inode
- */
-unsigned long btrfs_force_ra(struct address_space *mapping,
-                             struct file_ra_state *ra, struct file *file,
-                             pgoff_t offset, pgoff_t last_index)
-{
-       pgoff_t req_size = last_index - offset + 1;
-
-       page_cache_sync_readahead(mapping, ra, file, offset, req_size);
-       return offset + req_size;
-}
-
 struct inode *btrfs_alloc_inode(struct super_block *sb)
 {
        struct btrfs_inode *ei;
@@ -7164,7 +7210,11 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
                goto out_unlock;
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path) {
+               err = -ENOMEM;
+               drop_inode = 1;
+               goto out_unlock;
+       }
        key.objectid = btrfs_ino(inode);
        key.offset = 0;
        btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
@@ -7430,4 +7480,5 @@ static const struct inode_operations btrfs_symlink_inode_operations = {
 
 const struct dentry_operations btrfs_dentry_operations = {
        .d_delete       = btrfs_dentry_delete,
+       .d_release      = btrfs_dentry_release,
 };
index 0b980afc5eddfd4aeec80162e2eebecadd4015de..7cf013349941f7cb8509ee075d94f9099778c905 100644 (file)
@@ -1749,11 +1749,10 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info,
                key.objectid = key.offset;
                key.offset = (u64)-1;
                dirid = key.objectid;
-
        }
        if (ptr < name)
                goto out;
-       memcpy(name, ptr, total_len);
+       memmove(name, ptr, total_len);
        name[total_len]='\0';
        ret = 0;
 out:
diff --git a/fs/btrfs/ref-cache.c b/fs/btrfs/ref-cache.c
deleted file mode 100644 (file)
index 82d569c..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2008 Oracle.  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 v2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 021110-1307, USA.
- */
-
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/sort.h>
-#include "ctree.h"
-#include "ref-cache.h"
-#include "transaction.h"
-
-static struct rb_node *tree_insert(struct rb_root *root, u64 bytenr,
-                                  struct rb_node *node)
-{
-       struct rb_node **p = &root->rb_node;
-       struct rb_node *parent = NULL;
-       struct btrfs_leaf_ref *entry;
-
-       while (*p) {
-               parent = *p;
-               entry = rb_entry(parent, struct btrfs_leaf_ref, rb_node);
-
-               if (bytenr < entry->bytenr)
-                       p = &(*p)->rb_left;
-               else if (bytenr > entry->bytenr)
-                       p = &(*p)->rb_right;
-               else
-                       return parent;
-       }
-
-       entry = rb_entry(node, struct btrfs_leaf_ref, rb_node);
-       rb_link_node(node, parent, p);
-       rb_insert_color(node, root);
-       return NULL;
-}
-
-static struct rb_node *tree_search(struct rb_root *root, u64 bytenr)
-{
-       struct rb_node *n = root->rb_node;
-       struct btrfs_leaf_ref *entry;
-
-       while (n) {
-               entry = rb_entry(n, struct btrfs_leaf_ref, rb_node);
-               WARN_ON(!entry->in_tree);
-
-               if (bytenr < entry->bytenr)
-                       n = n->rb_left;
-               else if (bytenr > entry->bytenr)
-                       n = n->rb_right;
-               else
-                       return n;
-       }
-       return NULL;
-}
diff --git a/fs/btrfs/ref-cache.h b/fs/btrfs/ref-cache.h
deleted file mode 100644 (file)
index 24f7001..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2008 Oracle.  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 v2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 021110-1307, USA.
- */
-#ifndef __REFCACHE__
-#define __REFCACHE__
-
-struct btrfs_extent_info {
-       /* bytenr and num_bytes find the extent in the extent allocation tree */
-       u64 bytenr;
-       u64 num_bytes;
-
-       /* objectid and offset find the back reference for the file */
-       u64 objectid;
-       u64 offset;
-};
-
-struct btrfs_leaf_ref {
-       struct rb_node rb_node;
-       struct btrfs_leaf_ref_tree *tree;
-       int in_tree;
-       atomic_t usage;
-
-       u64 root_gen;
-       u64 bytenr;
-       u64 owner;
-       u64 generation;
-       int nritems;
-
-       struct list_head list;
-       struct btrfs_extent_info extents[];
-};
-
-static inline size_t btrfs_leaf_ref_size(int nr_extents)
-{
-       return sizeof(struct btrfs_leaf_ref) +
-              sizeof(struct btrfs_extent_info) * nr_extents;
-}
-#endif
index ebe45443de064471fba64ae4e176b2f7a1b66f93..f4099904565a508807f9c43458a1cdab6200cc24 100644 (file)
@@ -71,13 +71,12 @@ out:
        return ret;
 }
 
-int btrfs_set_root_node(struct btrfs_root_item *item,
-                       struct extent_buffer *node)
+void btrfs_set_root_node(struct btrfs_root_item *item,
+                        struct extent_buffer *node)
 {
        btrfs_set_root_bytenr(item, node->start);
        btrfs_set_root_level(item, btrfs_header_level(node));
        btrfs_set_root_generation(item, btrfs_header_generation(node));
-       return 0;
 }
 
 /*
index eb55863bb4aee8a323783aa24536d17ec166f26d..7dc36fab4afc5b9df1c4655e455c8df8bd3ee39b 100644 (file)
@@ -216,17 +216,11 @@ static void wait_current_trans(struct btrfs_root *root)
        spin_lock(&root->fs_info->trans_lock);
        cur_trans = root->fs_info->running_transaction;
        if (cur_trans && cur_trans->blocked) {
-               DEFINE_WAIT(wait);
                atomic_inc(&cur_trans->use_count);
                spin_unlock(&root->fs_info->trans_lock);
-               while (1) {
-                       prepare_to_wait(&root->fs_info->transaction_wait, &wait,
-                                       TASK_UNINTERRUPTIBLE);
-                       if (!cur_trans->blocked)
-                               break;
-                       schedule();
-               }
-               finish_wait(&root->fs_info->transaction_wait, &wait);
+
+               wait_event(root->fs_info->transaction_wait,
+                          !cur_trans->blocked);
                put_transaction(cur_trans);
        } else {
                spin_unlock(&root->fs_info->trans_lock);
@@ -357,19 +351,10 @@ struct btrfs_trans_handle *btrfs_start_ioctl_transaction(struct btrfs_root *root
 }
 
 /* wait for a transaction commit to be fully complete */
-static noinline int wait_for_commit(struct btrfs_root *root,
+static noinline void wait_for_commit(struct btrfs_root *root,
                                    struct btrfs_transaction *commit)
 {
-       DEFINE_WAIT(wait);
-       while (!commit->commit_done) {
-               prepare_to_wait(&commit->commit_wait, &wait,
-                               TASK_UNINTERRUPTIBLE);
-               if (commit->commit_done)
-                       break;
-               schedule();
-       }
-       finish_wait(&commit->commit_wait, &wait);
-       return 0;
+       wait_event(commit->commit_wait, commit->commit_done);
 }
 
 int btrfs_wait_for_commit(struct btrfs_root *root, u64 transid)
@@ -1085,22 +1070,7 @@ int btrfs_transaction_blocked(struct btrfs_fs_info *info)
 static void wait_current_trans_commit_start(struct btrfs_root *root,
                                            struct btrfs_transaction *trans)
 {
-       DEFINE_WAIT(wait);
-
-       if (trans->in_commit)
-               return;
-
-       while (1) {
-               prepare_to_wait(&root->fs_info->transaction_blocked_wait, &wait,
-                               TASK_UNINTERRUPTIBLE);
-               if (trans->in_commit) {
-                       finish_wait(&root->fs_info->transaction_blocked_wait,
-                                   &wait);
-                       break;
-               }
-               schedule();
-               finish_wait(&root->fs_info->transaction_blocked_wait, &wait);
-       }
+       wait_event(root->fs_info->transaction_blocked_wait, trans->in_commit);
 }
 
 /*
@@ -1110,24 +1080,8 @@ static void wait_current_trans_commit_start(struct btrfs_root *root,
 static void wait_current_trans_commit_start_and_unblock(struct btrfs_root *root,
                                         struct btrfs_transaction *trans)
 {
-       DEFINE_WAIT(wait);
-
-       if (trans->commit_done || (trans->in_commit && !trans->blocked))
-               return;
-
-       while (1) {
-               prepare_to_wait(&root->fs_info->transaction_wait, &wait,
-                               TASK_UNINTERRUPTIBLE);
-               if (trans->commit_done ||
-                   (trans->in_commit && !trans->blocked)) {
-                       finish_wait(&root->fs_info->transaction_wait,
-                                   &wait);
-                       break;
-               }
-               schedule();
-               finish_wait(&root->fs_info->transaction_wait,
-                           &wait);
-       }
+       wait_event(root->fs_info->transaction_wait,
+                  trans->commit_done || (trans->in_commit && !trans->blocked));
 }
 
 /*
@@ -1234,8 +1188,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
                atomic_inc(&cur_trans->use_count);
                btrfs_end_transaction(trans, root);
 
-               ret = wait_for_commit(root, cur_trans);
-               BUG_ON(ret);
+               wait_for_commit(root, cur_trans);
 
                put_transaction(cur_trans);
 
index ac278dd83175e3a64a573f9a16e154a21226ecda..babee65f8edaf779e26bc134ad7d02e8541ff8f2 100644 (file)
@@ -1617,7 +1617,8 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,
                return 0;
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return -ENOMEM;
 
        nritems = btrfs_header_nritems(eb);
        for (i = 0; i < nritems; i++) {
@@ -1723,7 +1724,9 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
                        return -ENOMEM;
 
                if (*level == 1) {
-                       wc->process_func(root, next, wc, ptr_gen);
+                       ret = wc->process_func(root, next, wc, ptr_gen);
+                       if (ret)
+                               return ret;
 
                        path->slots[*level]++;
                        if (wc->free) {
@@ -1788,8 +1791,11 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
                                parent = path->nodes[*level + 1];
 
                        root_owner = btrfs_header_owner(parent);
-                       wc->process_func(root, path->nodes[*level], wc,
+                       ret = wc->process_func(root, path->nodes[*level], wc,
                                 btrfs_header_generation(path->nodes[*level]));
+                       if (ret)
+                               return ret;
+
                        if (wc->free) {
                                struct extent_buffer *next;
 
index b89e372c75449bf228f7ca6eb5904d5b00474b90..53875ae73ad4f634548e8d1d315056822c9cbd4b 100644 (file)
@@ -1037,7 +1037,8 @@ static noinline int find_next_chunk(struct btrfs_root *root,
        struct btrfs_key found_key;
 
        path = btrfs_alloc_path();
-       BUG_ON(!path);
+       if (!path)
+               return -ENOMEM;
 
        key.objectid = objectid;
        key.offset = (u64)-1;
@@ -2061,8 +2062,10 @@ int btrfs_balance(struct btrfs_root *dev_root)
 
        /* step two, relocate all the chunks */
        path = btrfs_alloc_path();
-       BUG_ON(!path);
-
+       if (!path) {
+               ret = -ENOMEM;
+               goto error;
+       }
        key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
        key.offset = (u64)-1;
        key.type = BTRFS_CHUNK_ITEM_KEY;
@@ -2661,7 +2664,8 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans,
 
        ret = find_next_chunk(fs_info->chunk_root,
                              BTRFS_FIRST_CHUNK_TREE_OBJECTID, &chunk_offset);
-       BUG_ON(ret);
+       if (ret)
+               return ret;
 
        alloc_profile = BTRFS_BLOCK_GROUP_METADATA |
                        (fs_info->metadata_alloc_profile &
index b05aac3a8cfc9e35ad8eda4b31f18793fede8f38..c83cae19161e93b8fa5032dba84e46d87b2e90bf 100644 (file)
@@ -301,6 +301,27 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent)
        return parent;
 }
 
+/*
+ * Unhash a dentry without inserting an RCU walk barrier or checking that
+ * dentry->d_lock is locked.  The caller must take care of that, if
+ * appropriate.
+ */
+static void __d_shrink(struct dentry *dentry)
+{
+       if (!d_unhashed(dentry)) {
+               struct hlist_bl_head *b;
+               if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED))
+                       b = &dentry->d_sb->s_anon;
+               else
+                       b = d_hash(dentry->d_parent, dentry->d_name.hash);
+
+               hlist_bl_lock(b);
+               __hlist_bl_del(&dentry->d_hash);
+               dentry->d_hash.pprev = NULL;
+               hlist_bl_unlock(b);
+       }
+}
+
 /**
  * d_drop - drop a dentry
  * @dentry: dentry to drop
@@ -319,17 +340,7 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent)
 void __d_drop(struct dentry *dentry)
 {
        if (!d_unhashed(dentry)) {
-               struct hlist_bl_head *b;
-               if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED))
-                       b = &dentry->d_sb->s_anon;
-               else
-                       b = d_hash(dentry->d_parent, dentry->d_name.hash);
-
-               hlist_bl_lock(b);
-               __hlist_bl_del(&dentry->d_hash);
-               dentry->d_hash.pprev = NULL;
-               hlist_bl_unlock(b);
-
+               __d_shrink(dentry);
                dentry_rcuwalk_barrier(dentry);
        }
 }
@@ -784,6 +795,7 @@ relock:
 
 /**
  * prune_dcache_sb - shrink the dcache
+ * @sb: superblock
  * @nr_to_scan: number of entries to try to free
  *
  * Attempt to shrink the superblock dcache LRU by @nr_to_scan entries. This is
@@ -828,44 +840,24 @@ EXPORT_SYMBOL(shrink_dcache_sb);
 static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
 {
        struct dentry *parent;
-       unsigned detached = 0;
 
        BUG_ON(!IS_ROOT(dentry));
 
-       /* detach this root from the system */
-       spin_lock(&dentry->d_lock);
-       dentry_lru_del(dentry);
-       __d_drop(dentry);
-       spin_unlock(&dentry->d_lock);
-
        for (;;) {
                /* descend to the first leaf in the current subtree */
-               while (!list_empty(&dentry->d_subdirs)) {
-                       struct dentry *loop;
-
-                       /* this is a branch with children - detach all of them
-                        * from the system in one go */
-                       spin_lock(&dentry->d_lock);
-                       list_for_each_entry(loop, &dentry->d_subdirs,
-                                           d_u.d_child) {
-                               spin_lock_nested(&loop->d_lock,
-                                               DENTRY_D_LOCK_NESTED);
-                               dentry_lru_del(loop);
-                               __d_drop(loop);
-                               spin_unlock(&loop->d_lock);
-                       }
-                       spin_unlock(&dentry->d_lock);
-
-                       /* move to the first child */
+               while (!list_empty(&dentry->d_subdirs))
                        dentry = list_entry(dentry->d_subdirs.next,
                                            struct dentry, d_u.d_child);
-               }
 
                /* consume the dentries from this leaf up through its parents
                 * until we find one with children or run out altogether */
                do {
                        struct inode *inode;
 
+                       /* detach from the system */
+                       dentry_lru_del(dentry);
+                       __d_shrink(dentry);
+
                        if (dentry->d_count != 0) {
                                printk(KERN_ERR
                                       "BUG: Dentry %p{i=%lx,n=%s}"
@@ -886,14 +878,10 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
                                list_del(&dentry->d_u.d_child);
                        } else {
                                parent = dentry->d_parent;
-                               spin_lock(&parent->d_lock);
                                parent->d_count--;
                                list_del(&dentry->d_u.d_child);
-                               spin_unlock(&parent->d_lock);
                        }
 
-                       detached++;
-
                        inode = dentry->d_inode;
                        if (inode) {
                                dentry->d_inode = NULL;
@@ -938,9 +926,7 @@ void shrink_dcache_for_umount(struct super_block *sb)
 
        dentry = sb->s_root;
        sb->s_root = NULL;
-       spin_lock(&dentry->d_lock);
        dentry->d_count--;
-       spin_unlock(&dentry->d_lock);
        shrink_dcache_for_umount_subtree(dentry);
 
        while (!hlist_bl_empty(&sb->s_anon)) {
index 52c053763942eb0db19f72b7fb4618bbeeffd012..35d6a3cfd9ff3a5c6562adb46fc614595ff4c718 100644 (file)
@@ -194,12 +194,10 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
                case ACL_TYPE_ACCESS:
                        name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
                        if (acl) {
-                               mode_t mode = inode->i_mode;
-                               error = posix_acl_equiv_mode(acl, &mode);
+                               error = posix_acl_equiv_mode(acl, &inode->i_mode);
                                if (error < 0)
                                        return error;
                                else {
-                                       inode->i_mode = mode;
                                        inode->i_ctime = CURRENT_TIME_SEC;
                                        mark_inode_dirty(inode);
                                        if (error == 0)
@@ -253,16 +251,14 @@ ext2_init_acl(struct inode *inode, struct inode *dir)
                        inode->i_mode &= ~current_umask();
        }
        if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
-               mode_t mode = inode->i_mode;
                if (S_ISDIR(inode->i_mode)) {
                        error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
                        if (error)
                                goto cleanup;
                }
-               error = posix_acl_create(&acl, GFP_KERNEL, &mode);
+               error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode);
                if (error < 0)
                        return error;
-               inode->i_mode = mode;
                if (error > 0) {
                        /* This is an extended ACL */
                        error = ext2_set_acl(inode, ACL_TYPE_ACCESS, acl);
index 6c29bf0df04a0d0ed619a3b886e2d24847ab7a45..3091f62e55b680ae567817c50a6bda3161753895 100644 (file)
@@ -199,12 +199,10 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type,
                case ACL_TYPE_ACCESS:
                        name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS;
                        if (acl) {
-                               mode_t mode = inode->i_mode;
-                               error = posix_acl_equiv_mode(acl, &mode);
+                               error = posix_acl_equiv_mode(acl, &inode->i_mode);
                                if (error < 0)
                                        return error;
                                else {
-                                       inode->i_mode = mode;
                                        inode->i_ctime = CURRENT_TIME_SEC;
                                        ext3_mark_inode_dirty(handle, inode);
                                        if (error == 0)
@@ -261,19 +259,16 @@ ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
                        inode->i_mode &= ~current_umask();
        }
        if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
-               mode_t mode = inode->i_mode;
-
                if (S_ISDIR(inode->i_mode)) {
                        error = ext3_set_acl(handle, inode,
                                             ACL_TYPE_DEFAULT, acl);
                        if (error)
                                goto cleanup;
                }
-               error = posix_acl_create(&acl, GFP_NOFS, &mode);
+               error = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
                if (error < 0)
                        return error;
 
-               inode->i_mode = mode;
                if (error > 0) {
                        /* This is an extended ACL */
                        error = ext3_set_acl(handle, inode, ACL_TYPE_ACCESS, acl);
index 04109460ba9e3f22d0115f8cc8b16d4e9b13d3f6..56fd8f865930e8347e0d48b67b9e5435970efab4 100644 (file)
@@ -7,7 +7,7 @@ obj-$(CONFIG_EXT4_FS) += ext4.o
 ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \
                ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \
                ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.o \
-               mmp.o
+               mmp.o indirect.o
 
 ext4-$(CONFIG_EXT4_FS_XATTR)           += xattr.o xattr_user.o xattr_trusted.o
 ext4-$(CONFIG_EXT4_FS_POSIX_ACL)       += acl.o
index dca2d1ded931eafbad76cda7cd9b0f07bf7fa6f8..a5c29bb3b835d7c528d0b9f65ce3436e8b7f9181 100644 (file)
@@ -198,12 +198,10 @@ ext4_set_acl(handle_t *handle, struct inode *inode, int type,
        case ACL_TYPE_ACCESS:
                name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
                if (acl) {
-                       mode_t mode = inode->i_mode;
-                       error = posix_acl_equiv_mode(acl, &mode);
+                       error = posix_acl_equiv_mode(acl, &inode->i_mode);
                        if (error < 0)
                                return error;
                        else {
-                               inode->i_mode = mode;
                                inode->i_ctime = ext4_current_time(inode);
                                ext4_mark_inode_dirty(handle, inode);
                                if (error == 0)
@@ -259,19 +257,16 @@ ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
                        inode->i_mode &= ~current_umask();
        }
        if (test_opt(inode->i_sb, POSIX_ACL) && acl) {
-               mode_t mode = inode->i_mode;
-
                if (S_ISDIR(inode->i_mode)) {
                        error = ext4_set_acl(handle, inode,
                                             ACL_TYPE_DEFAULT, acl);
                        if (error)
                                goto cleanup;
                }
-               error = posix_acl_create(&acl, GFP_NOFS, &mode);
+               error = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
                if (error < 0)
                        return error;
 
-               inode->i_mode = mode;
                if (error > 0) {
                        /* This is an extended ACL */
                        error = ext4_set_acl(handle, inode, ACL_TYPE_ACCESS, acl);
index 264f6949511ef842169438571049ccb736955531..f8224adf496ed91def8db62a83ebe6fb85d890ee 100644 (file)
@@ -620,3 +620,51 @@ unsigned long ext4_bg_num_gdb(struct super_block *sb, ext4_group_t group)
 
 }
 
+/**
+ *     ext4_inode_to_goal_block - return a hint for block allocation
+ *     @inode: inode for block allocation
+ *
+ *     Return the ideal location to start allocating blocks for a
+ *     newly created inode.
+ */
+ext4_fsblk_t ext4_inode_to_goal_block(struct inode *inode)
+{
+       struct ext4_inode_info *ei = EXT4_I(inode);
+       ext4_group_t block_group;
+       ext4_grpblk_t colour;
+       int flex_size = ext4_flex_bg_size(EXT4_SB(inode->i_sb));
+       ext4_fsblk_t bg_start;
+       ext4_fsblk_t last_block;
+
+       block_group = ei->i_block_group;
+       if (flex_size >= EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME) {
+               /*
+                * If there are at least EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME
+                * block groups per flexgroup, reserve the first block
+                * group for directories and special files.  Regular
+                * files will start at the second block group.  This
+                * tends to speed up directory access and improves
+                * fsck times.
+                */
+               block_group &= ~(flex_size-1);
+               if (S_ISREG(inode->i_mode))
+                       block_group++;
+       }
+       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;
+
+       /*
+        * If we are doing delayed allocation, we don't need take
+        * colour into account.
+        */
+       if (test_opt(inode->i_sb, DELALLOC))
+               return bg_start;
+
+       if (bg_start + EXT4_BLOCKS_PER_GROUP(inode->i_sb) <= last_block)
+               colour = (current->pid % 16) *
+                       (EXT4_BLOCKS_PER_GROUP(inode->i_sb) / 16);
+       else
+               colour = (current->pid % 16) * ((last_block - bg_start) / 16);
+       return bg_start + colour;
+}
+
index fac90f3fba80759b5e42c6902dba00c8c16d1286..8efb2f0a344763a2421204e935af1e09897239be 100644 (file)
@@ -246,3 +246,24 @@ int ext4_data_block_valid(struct ext4_sb_info *sbi, ext4_fsblk_t start_blk,
        return 1;
 }
 
+int ext4_check_blockref(const char *function, unsigned int line,
+                       struct inode *inode, __le32 *p, unsigned int max)
+{
+       struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
+       __le32 *bref = p;
+       unsigned int blk;
+
+       while (bref < p+max) {
+               blk = le32_to_cpu(*bref++);
+               if (blk &&
+                   unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb),
+                                                   blk, 1))) {
+                       es->s_last_error_block = cpu_to_le64(blk);
+                       ext4_error_inode(inode, function, line, blk,
+                                        "invalid block");
+                       return -EIO;
+               }
+       }
+       return 0;
+}
+
index fa44df8797115f97f3e098b512ba8b7c7a2a2de5..e717dfd2f2b4b2e1871e4c66452202b89191ba61 100644 (file)
@@ -526,6 +526,7 @@ struct ext4_new_group_data {
 #define EXT4_FREE_BLOCKS_METADATA      0x0001
 #define EXT4_FREE_BLOCKS_FORGET                0x0002
 #define EXT4_FREE_BLOCKS_VALIDATED     0x0004
+#define EXT4_FREE_BLOCKS_NO_QUOT_UPDATE        0x0008
 
 /*
  * ioctl commands
@@ -939,6 +940,8 @@ struct ext4_inode_info {
 #define ext4_find_next_zero_bit                find_next_zero_bit_le
 #define ext4_find_next_bit             find_next_bit_le
 
+extern void ext4_set_bits(void *bm, int cur, int len);
+
 /*
  * Maximal mount counts between two filesystem checks
  */
@@ -1126,7 +1129,8 @@ struct ext4_sb_info {
        struct journal_s *s_journal;
        struct list_head s_orphan;
        struct mutex s_orphan_lock;
-       struct mutex s_resize_lock;
+       unsigned long s_resize_flags;           /* Flags indicating if there
+                                                  is a resizer */
        unsigned long s_commit_interval;
        u32 s_max_batch_time;
        u32 s_min_batch_time;
@@ -1214,6 +1218,9 @@ struct ext4_sb_info {
 
        /* Kernel thread for multiple mount protection */
        struct task_struct *s_mmp_tsk;
+
+       /* record the last minlen when FITRIM is called. */
+       atomic_t s_last_trim_minblks;
 };
 
 static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
@@ -1743,6 +1750,7 @@ extern unsigned ext4_init_block_bitmap(struct super_block *sb,
                                       struct ext4_group_desc *desc);
 #define ext4_free_blocks_after_init(sb, group, desc)                   \
                ext4_init_block_bitmap(sb, NULL, group, desc)
+ext4_fsblk_t ext4_inode_to_goal_block(struct inode *);
 
 /* dir.c */
 extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *,
@@ -1793,7 +1801,7 @@ extern void ext4_free_blocks(handle_t *handle, struct inode *inode,
                             unsigned long count, int flags);
 extern int ext4_mb_add_groupinfo(struct super_block *sb,
                ext4_group_t i, struct ext4_group_desc *desc);
-extern void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
+extern int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
                                ext4_fsblk_t block, unsigned long count);
 extern int ext4_trim_fs(struct super_block *, struct fstrim_range *);
 
@@ -1834,6 +1842,17 @@ 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 void ext4_da_update_reserve_space(struct inode *inode,
                                        int used, int quota_claim);
+
+/* indirect.c */
+extern int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
+                               struct ext4_map_blocks *map, int flags);
+extern ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb,
+                               const struct iovec *iov, loff_t offset,
+                               unsigned long nr_segs);
+extern int ext4_ind_calc_metadata_amount(struct inode *inode, sector_t lblock);
+extern int ext4_ind_trans_blocks(struct inode *inode, int nrblocks, int chunk);
+extern void ext4_ind_truncate(struct inode *inode);
+
 /* ioctl.c */
 extern long ext4_ioctl(struct file *, unsigned int, unsigned long);
 extern long ext4_compat_ioctl(struct file *, unsigned int, unsigned long);
@@ -1855,6 +1874,9 @@ extern int ext4_group_extend(struct super_block *sb,
                                ext4_fsblk_t n_blocks_count);
 
 /* super.c */
+extern void *ext4_kvmalloc(size_t size, gfp_t flags);
+extern void *ext4_kvzalloc(size_t size, gfp_t flags);
+extern void ext4_kvfree(void *ptr);
 extern void __ext4_error(struct super_block *, const char *, unsigned int,
                         const char *, ...)
        __attribute__ ((format (printf, 4, 5)));
@@ -2067,11 +2089,19 @@ struct ext4_group_info {
                                         * 5 free 8-block regions. */
 };
 
-#define EXT4_GROUP_INFO_NEED_INIT_BIT  0
+#define EXT4_GROUP_INFO_NEED_INIT_BIT          0
+#define EXT4_GROUP_INFO_WAS_TRIMMED_BIT                1
 
 #define EXT4_MB_GRP_NEED_INIT(grp)     \
        (test_bit(EXT4_GROUP_INFO_NEED_INIT_BIT, &((grp)->bb_state)))
 
+#define EXT4_MB_GRP_WAS_TRIMMED(grp)   \
+       (test_bit(EXT4_GROUP_INFO_WAS_TRIMMED_BIT, &((grp)->bb_state)))
+#define EXT4_MB_GRP_SET_TRIMMED(grp)   \
+       (set_bit(EXT4_GROUP_INFO_WAS_TRIMMED_BIT, &((grp)->bb_state)))
+#define EXT4_MB_GRP_CLEAR_TRIMMED(grp) \
+       (clear_bit(EXT4_GROUP_INFO_WAS_TRIMMED_BIT, &((grp)->bb_state)))
+
 #define EXT4_MAX_CONTENTION            8
 #define EXT4_CONTENTION_THRESHOLD      2
 
@@ -2122,6 +2152,19 @@ static inline void ext4_mark_super_dirty(struct super_block *sb)
                sb->s_dirt =1;
 }
 
+/*
+ * Block validity checking
+ */
+#define ext4_check_indirect_blockref(inode, bh)                                \
+       ext4_check_blockref(__func__, __LINE__, inode,                  \
+                           (__le32 *)(bh)->b_data,                     \
+                           EXT4_ADDR_PER_BLOCK((inode)->i_sb))
+
+#define ext4_ind_check_inode(inode)                                    \
+       ext4_check_blockref(__func__, __LINE__, inode,                  \
+                           EXT4_I(inode)->i_data,                      \
+                           EXT4_NDIR_BLOCKS)
+
 /*
  * Inodes and files operations
  */
@@ -2151,6 +2194,8 @@ extern void ext4_exit_system_zone(void);
 extern int ext4_data_block_valid(struct ext4_sb_info *sbi,
                                 ext4_fsblk_t start_blk,
                                 unsigned int count);
+extern int ext4_check_blockref(const char *, unsigned int,
+                              struct inode *, __le32 *, unsigned int);
 
 /* extents.c */
 extern int ext4_ext_tree_init(handle_t *handle, struct inode *);
@@ -2230,6 +2275,10 @@ static inline void set_bitmap_uptodate(struct buffer_head *bh)
 extern wait_queue_head_t ext4__ioend_wq[EXT4_WQ_HASH_SZ];
 extern struct mutex ext4__aio_mutex[EXT4_WQ_HASH_SZ];
 
+#define EXT4_RESIZING  0
+extern int ext4_resize_begin(struct super_block *sb);
+extern void ext4_resize_end(struct super_block *sb);
+
 #endif /* __KERNEL__ */
 
 #endif /* _EXT4_H */
index f815cc81e7a287bd7198dc874d7954938c188464..57cf568a98ab652afcfe3581d093d5f2b5758689 100644 (file)
@@ -114,12 +114,6 @@ static ext4_fsblk_t ext4_ext_find_goal(struct inode *inode,
                              struct ext4_ext_path *path,
                              ext4_lblk_t block)
 {
-       struct ext4_inode_info *ei = EXT4_I(inode);
-       ext4_fsblk_t bg_start;
-       ext4_fsblk_t last_block;
-       ext4_grpblk_t colour;
-       ext4_group_t block_group;
-       int flex_size = ext4_flex_bg_size(EXT4_SB(inode->i_sb));
        int depth;
 
        if (path) {
@@ -161,36 +155,7 @@ static ext4_fsblk_t ext4_ext_find_goal(struct inode *inode,
        }
 
        /* OK. use inode's group */
-       block_group = ei->i_block_group;
-       if (flex_size >= EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME) {
-               /*
-                * If there are at least EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME
-                * block groups per flexgroup, reserve the first block
-                * group for directories and special files.  Regular
-                * files will start at the second block group.  This
-                * tends to speed up directory access and improves
-                * fsck times.
-                */
-               block_group &= ~(flex_size-1);
-               if (S_ISREG(inode->i_mode))
-                       block_group++;
-       }
-       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;
-
-       /*
-        * If we are doing delayed allocation, we don't need take
-        * colour into account.
-        */
-       if (test_opt(inode->i_sb, DELALLOC))
-               return bg_start;
-
-       if (bg_start + EXT4_BLOCKS_PER_GROUP(inode->i_sb) <= last_block)
-               colour = (current->pid % 16) *
-                       (EXT4_BLOCKS_PER_GROUP(inode->i_sb) / 16);
-       else
-               colour = (current->pid % 16) * ((last_block - bg_start) / 16);
-       return bg_start + colour + block;
+       return ext4_inode_to_goal_block(inode);
 }
 
 /*
@@ -776,6 +741,16 @@ static int ext4_ext_insert_index(handle_t *handle, struct inode *inode,
                                 logical, le32_to_cpu(curp->p_idx->ei_block));
                return -EIO;
        }
+
+       if (unlikely(le16_to_cpu(curp->p_hdr->eh_entries)
+                            >= le16_to_cpu(curp->p_hdr->eh_max))) {
+               EXT4_ERROR_INODE(inode,
+                                "eh_entries %d >= eh_max %d!",
+                                le16_to_cpu(curp->p_hdr->eh_entries),
+                                le16_to_cpu(curp->p_hdr->eh_max));
+               return -EIO;
+       }
+
        len = EXT_MAX_INDEX(curp->p_hdr) - curp->p_idx;
        if (logical > le32_to_cpu(curp->p_idx->ei_block)) {
                /* insert after */
@@ -805,13 +780,6 @@ static 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);
 
-       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;
@@ -1446,8 +1414,7 @@ ext4_ext_next_allocated_block(struct ext4_ext_path *path)
  * ext4_ext_next_leaf_block:
  * returns first allocated block from next leaf or EXT_MAX_BLOCKS
  */
-static ext4_lblk_t ext4_ext_next_leaf_block(struct inode *inode,
-                                       struct ext4_ext_path *path)
+static ext4_lblk_t ext4_ext_next_leaf_block(struct ext4_ext_path *path)
 {
        int depth;
 
@@ -1757,7 +1724,6 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
                goto merge;
        }
 
-repeat:
        depth = ext_depth(inode);
        eh = path[depth].p_hdr;
        if (le16_to_cpu(eh->eh_entries) < le16_to_cpu(eh->eh_max))
@@ -1765,9 +1731,10 @@ repeat:
 
        /* probably next leaf has space for us? */
        fex = EXT_LAST_EXTENT(eh);
-       next = ext4_ext_next_leaf_block(inode, path);
-       if (le32_to_cpu(newext->ee_block) > le32_to_cpu(fex->ee_block)
-           && next != EXT_MAX_BLOCKS) {
+       next = EXT_MAX_BLOCKS;
+       if (le32_to_cpu(newext->ee_block) > le32_to_cpu(fex->ee_block))
+               next = ext4_ext_next_leaf_block(path);
+       if (next != EXT_MAX_BLOCKS) {
                ext_debug("next leaf block - %d\n", next);
                BUG_ON(npath != NULL);
                npath = ext4_ext_find_extent(inode, next, NULL);
@@ -1779,7 +1746,7 @@ repeat:
                        ext_debug("next leaf isn't full(%d)\n",
                                  le16_to_cpu(eh->eh_entries));
                        path = npath;
-                       goto repeat;
+                       goto has_space;
                }
                ext_debug("next leaf has no free space(%d,%d)\n",
                          le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max));
@@ -1839,7 +1806,7 @@ has_space:
                                ext4_ext_pblock(newext),
                                ext4_ext_is_uninitialized(newext),
                                ext4_ext_get_actual_len(newext),
-                               nearex, len, nearex + 1, nearex + 2);
+                               nearex, len, nearex, nearex + 1);
                memmove(nearex + 1, nearex, len);
                path[depth].p_ext = nearex;
        }
@@ -2052,7 +2019,7 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path,
 }
 
 /*
- * ext4_ext_in_cache()
+ * ext4_ext_check_cache()
  * Checks to see if the given block is in the cache.
  * If it is, the cached extent is stored in the given
  * cache extent pointer.  If the cached extent is a hole,
@@ -2134,8 +2101,6 @@ ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
 /*
  * ext4_ext_rm_idx:
  * removes index from the index block.
- * It's used in truncate case only, thus all requests are for
- * last index in the block only.
  */
 static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
                        struct ext4_ext_path *path)
@@ -2153,6 +2118,13 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
        err = ext4_ext_get_access(handle, inode, path);
        if (err)
                return err;
+
+       if (path->p_idx != EXT_LAST_INDEX(path->p_hdr)) {
+               int len = EXT_LAST_INDEX(path->p_hdr) - path->p_idx;
+               len *= sizeof(struct ext4_extent_idx);
+               memmove(path->p_idx, path->p_idx + 1, len);
+       }
+
        le16_add_cpu(&path->p_hdr->eh_entries, -1);
        err = ext4_ext_dirty(handle, inode, path);
        if (err)
@@ -2534,8 +2506,7 @@ ext4_ext_more_to_rm(struct ext4_ext_path *path)
        return 1;
 }
 
-static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
-                               ext4_lblk_t end)
+static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
 {
        struct super_block *sb = inode->i_sb;
        int depth = ext_depth(inode);
@@ -2575,7 +2546,7 @@ again:
                if (i == depth) {
                        /* this is leaf block */
                        err = ext4_ext_rm_leaf(handle, inode, path,
-                                       start, end);
+                                       start, EXT_MAX_BLOCKS - 1);
                        /* root level has p_bh == NULL, brelse() eats this */
                        brelse(path[i].p_bh);
                        path[i].p_bh = NULL;
@@ -3107,12 +3078,10 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
                                              struct ext4_ext_path *path)
 {
        struct ext4_extent *ex;
-       struct ext4_extent_header *eh;
        int depth;
        int err = 0;
 
        depth = ext_depth(inode);
-       eh = path[depth].p_hdr;
        ex = path[depth].p_ext;
 
        ext_debug("ext4_convert_unwritten_extents_endio: inode %lu, logical"
@@ -3357,8 +3326,8 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
        trace_ext4_ext_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
 
        /* check in cache */
-       if (ext4_ext_in_cache(inode, map->m_lblk, &newex) &&
-               ((flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) == 0)) {
+       if (!(flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) &&
+               ext4_ext_in_cache(inode, map->m_lblk, &newex)) {
                if (!newex.ee_start_lo && !newex.ee_start_hi) {
                        if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) {
                                /*
@@ -3497,8 +3466,27 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
 
                        ext4_ext_mark_uninitialized(ex);
 
-                       err = ext4_ext_remove_space(inode, map->m_lblk,
-                               map->m_lblk + punched_out);
+                       ext4_ext_invalidate_cache(inode);
+
+                       err = ext4_ext_rm_leaf(handle, inode, path,
+                               map->m_lblk, map->m_lblk + punched_out);
+
+                       if (!err && path->p_hdr->eh_entries == 0) {
+                               /*
+                                * Punch hole freed all of this sub tree,
+                                * so we need to correct eh_depth
+                                */
+                               err = ext4_ext_get_access(handle, inode, path);
+                               if (err == 0) {
+                                       ext_inode_hdr(inode)->eh_depth = 0;
+                                       ext_inode_hdr(inode)->eh_max =
+                                       cpu_to_le16(ext4_ext_space_root(
+                                               inode, 0));
+
+                                       err = ext4_ext_dirty(
+                                               handle, inode, path);
+                               }
+                       }
 
                        goto out2;
                }
@@ -3596,17 +3584,18 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
        }
 
        err = check_eofblocks_fl(handle, inode, map->m_lblk, path, ar.len);
-       if (err)
-               goto out2;
-
-       err = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
+       if (!err)
+               err = ext4_ext_insert_extent(handle, inode, path,
+                                            &newex, flags);
        if (err) {
+               int fb_flags = flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE ?
+                       EXT4_FREE_BLOCKS_NO_QUOT_UPDATE : 0;
                /* free data blocks we just allocated */
                /* not a good idea to call discard here directly,
                 * but otherwise we'd need to call it every free() */
                ext4_discard_preallocations(inode);
                ext4_free_blocks(handle, inode, NULL, ext4_ext_pblock(&newex),
-                                ext4_ext_get_actual_len(&newex), 0);
+                                ext4_ext_get_actual_len(&newex), fb_flags);
                goto out2;
        }
 
@@ -3699,7 +3688,7 @@ void ext4_ext_truncate(struct inode *inode)
 
        last_block = (inode->i_size + sb->s_blocksize - 1)
                        >> EXT4_BLOCK_SIZE_BITS(sb);
-       err = ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCKS - 1);
+       err = ext4_ext_remove_space(inode, last_block);
 
        /* In a multi-transaction truncate, we only make the final
         * transaction synchronous.
@@ -3835,7 +3824,7 @@ retry:
                                                blkbits) >> blkbits))
                        new_size = offset + len;
                else
-                       new_size = (map.m_lblk + ret) << blkbits;
+                       new_size = ((loff_t) map.m_lblk + ret) << blkbits;
 
                ext4_falloc_update_inode(inode, mode, new_size,
                                         (map.m_flags & EXT4_MAP_NEW));
index da3bed3e0c29d1159f2169f4b94be97ce485d966..036f78f7a1ef92cfc34a281665cf53c588a4a64e 100644 (file)
@@ -129,15 +129,30 @@ static int ext4_sync_parent(struct inode *inode)
 {
        struct writeback_control wbc;
        struct dentry *dentry = NULL;
+       struct inode *next;
        int ret = 0;
 
-       while (inode && ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY)) {
+       if (!ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY))
+               return 0;
+       inode = igrab(inode);
+       while (ext4_test_inode_state(inode, EXT4_STATE_NEWENTRY)) {
                ext4_clear_inode_state(inode, EXT4_STATE_NEWENTRY);
-               dentry = list_entry(inode->i_dentry.next,
-                                   struct dentry, d_alias);
-               if (!dentry || !dentry->d_parent || !dentry->d_parent->d_inode)
+               dentry = NULL;
+               spin_lock(&inode->i_lock);
+               if (!list_empty(&inode->i_dentry)) {
+                       dentry = list_first_entry(&inode->i_dentry,
+                                                 struct dentry, d_alias);
+                       dget(dentry);
+               }
+               spin_unlock(&inode->i_lock);
+               if (!dentry)
                        break;
-               inode = dentry->d_parent->d_inode;
+               next = igrab(dentry->d_parent->d_inode);
+               dput(dentry);
+               if (!next)
+                       break;
+               iput(inode);
+               inode = next;
                ret = sync_mapping_buffers(inode->i_mapping);
                if (ret)
                        break;
@@ -148,6 +163,7 @@ static int ext4_sync_parent(struct inode *inode)
                if (ret)
                        break;
        }
+       iput(inode);
        return ret;
 }
 
index 21bb2f61e50223c2da0946c4b48db0e4c947e1a7..9c63f273b550497759103ad0f6b4aaafd8e2049d 100644 (file)
@@ -1287,7 +1287,7 @@ extern int ext4_init_inode_table(struct super_block *sb, ext4_group_t group,
                           group, used_blks,
                           ext4_itable_unused_count(sb, gdp));
                ret = 1;
-               goto out;
+               goto err_out;
        }
 
        blk = ext4_inode_table(sb, gdp) + used_blks;
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c
new file mode 100644 (file)
index 0000000..b8602cd
--- /dev/null
@@ -0,0 +1,1482 @@
+/*
+ *  linux/fs/ext4/indirect.c
+ *
+ *  from
+ *
+ *  linux/fs/ext4/inode.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ *  from
+ *
+ *  linux/fs/minix/inode.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Goal-directed block allocation by Stephen Tweedie
+ *     (sct@redhat.com), 1993, 1998
+ */
+
+#include <linux/module.h>
+#include "ext4_jbd2.h"
+#include "truncate.h"
+
+#include <trace/events/ext4.h>
+
+typedef struct {
+       __le32  *p;
+       __le32  key;
+       struct buffer_head *bh;
+} Indirect;
+
+static inline void add_chain(Indirect *p, struct buffer_head *bh, __le32 *v)
+{
+       p->key = *(p->p = v);
+       p->bh = bh;
+}
+
+/**
+ *     ext4_block_to_path - parse the block number into array of offsets
+ *     @inode: inode in question (we are only interested in its superblock)
+ *     @i_block: block number to be parsed
+ *     @offsets: array to store the offsets in
+ *     @boundary: set this non-zero if the referred-to block is likely to be
+ *            followed (on disk) by an indirect block.
+ *
+ *     To store the locations of file's data ext4 uses a data structure common
+ *     for UNIX filesystems - tree of pointers anchored in the inode, with
+ *     data blocks at leaves and indirect blocks in intermediate nodes.
+ *     This function translates the block number into path in that tree -
+ *     return value is the path length and @offsets[n] is the offset of
+ *     pointer to (n+1)th node in the nth one. If @block is out of range
+ *     (negative or too large) warning is printed and zero returned.
+ *
+ *     Note: function doesn't find node addresses, so no IO is needed. All
+ *     we need to know is the capacity of indirect blocks (taken from the
+ *     inode->i_sb).
+ */
+
+/*
+ * Portability note: the last comparison (check that we fit into triple
+ * indirect block) is spelled differently, because otherwise on an
+ * architecture with 32-bit longs and 8Kb pages we might get into trouble
+ * if our filesystem had 8Kb blocks. We might use long long, but that would
+ * kill us on x86. Oh, well, at least the sign propagation does not matter -
+ * i_block would have to be negative in the very beginning, so we would not
+ * get there at all.
+ */
+
+static int ext4_block_to_path(struct inode *inode,
+                             ext4_lblk_t i_block,
+                             ext4_lblk_t offsets[4], int *boundary)
+{
+       int ptrs = EXT4_ADDR_PER_BLOCK(inode->i_sb);
+       int ptrs_bits = EXT4_ADDR_PER_BLOCK_BITS(inode->i_sb);
+       const long direct_blocks = EXT4_NDIR_BLOCKS,
+               indirect_blocks = ptrs,
+               double_blocks = (1 << (ptrs_bits * 2));
+       int n = 0;
+       int final = 0;
+
+       if (i_block < direct_blocks) {
+               offsets[n++] = i_block;
+               final = direct_blocks;
+       } else if ((i_block -= direct_blocks) < indirect_blocks) {
+               offsets[n++] = EXT4_IND_BLOCK;
+               offsets[n++] = i_block;
+               final = ptrs;
+       } else if ((i_block -= indirect_blocks) < double_blocks) {
+               offsets[n++] = EXT4_DIND_BLOCK;
+               offsets[n++] = i_block >> ptrs_bits;
+               offsets[n++] = i_block & (ptrs - 1);
+               final = ptrs;
+       } else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
+               offsets[n++] = EXT4_TIND_BLOCK;
+               offsets[n++] = i_block >> (ptrs_bits * 2);
+               offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
+               offsets[n++] = i_block & (ptrs - 1);
+               final = ptrs;
+       } else {
+               ext4_warning(inode->i_sb, "block %lu > max in inode %lu",
+                            i_block + direct_blocks +
+                            indirect_blocks + double_blocks, inode->i_ino);
+       }
+       if (boundary)
+               *boundary = final - 1 - (i_block & (ptrs - 1));
+       return n;
+}
+
+/**
+ *     ext4_get_branch - read the chain of indirect blocks leading to data
+ *     @inode: inode in question
+ *     @depth: depth of the chain (1 - direct pointer, etc.)
+ *     @offsets: offsets of pointers in inode/indirect blocks
+ *     @chain: place to store the result
+ *     @err: here we store the error value
+ *
+ *     Function fills the array of triples <key, p, bh> and returns %NULL
+ *     if everything went OK or the pointer to the last filled triple
+ *     (incomplete one) otherwise. Upon the return chain[i].key contains
+ *     the number of (i+1)-th block in the chain (as it is stored in memory,
+ *     i.e. little-endian 32-bit), chain[i].p contains the address of that
+ *     number (it points into struct inode for i==0 and into the bh->b_data
+ *     for i>0) and chain[i].bh points to the buffer_head of i-th indirect
+ *     block for i>0 and NULL for i==0. In other words, it holds the block
+ *     numbers of the chain, addresses they were taken from (and where we can
+ *     verify that chain did not change) and buffer_heads hosting these
+ *     numbers.
+ *
+ *     Function stops when it stumbles upon zero pointer (absent block)
+ *             (pointer to last triple returned, *@err == 0)
+ *     or when it gets an IO error reading an indirect block
+ *             (ditto, *@err == -EIO)
+ *     or when it reads all @depth-1 indirect blocks successfully and finds
+ *     the whole chain, all way to the data (returns %NULL, *err == 0).
+ *
+ *      Need to be called with
+ *      down_read(&EXT4_I(inode)->i_data_sem)
+ */
+static Indirect *ext4_get_branch(struct inode *inode, int depth,
+                                ext4_lblk_t  *offsets,
+                                Indirect chain[4], int *err)
+{
+       struct super_block *sb = inode->i_sb;
+       Indirect *p = chain;
+       struct buffer_head *bh;
+
+       *err = 0;
+       /* i_data is not going away, no lock needed */
+       add_chain(chain, NULL, EXT4_I(inode)->i_data + *offsets);
+       if (!p->key)
+               goto no_block;
+       while (--depth) {
+               bh = sb_getblk(sb, le32_to_cpu(p->key));
+               if (unlikely(!bh))
+                       goto failure;
+
+               if (!bh_uptodate_or_lock(bh)) {
+                       if (bh_submit_read(bh) < 0) {
+                               put_bh(bh);
+                               goto failure;
+                       }
+                       /* validate block references */
+                       if (ext4_check_indirect_blockref(inode, bh)) {
+                               put_bh(bh);
+                               goto failure;
+                       }
+               }
+
+               add_chain(++p, bh, (__le32 *)bh->b_data + *++offsets);
+               /* Reader: end */
+               if (!p->key)
+                       goto no_block;
+       }
+       return NULL;
+
+failure:
+       *err = -EIO;
+no_block:
+       return p;
+}
+
+/**
+ *     ext4_find_near - find a place for allocation with sufficient locality
+ *     @inode: owner
+ *     @ind: descriptor of indirect block.
+ *
+ *     This function returns the preferred place for block allocation.
+ *     It is used when heuristic for sequential allocation fails.
+ *     Rules are:
+ *       + if there is a block to the left of our position - allocate near it.
+ *       + if pointer will live in indirect block - allocate near that block.
+ *       + if pointer will live in inode - allocate in the same
+ *         cylinder group.
+ *
+ * In the latter case we colour the starting block by the callers PID to
+ * prevent it from clashing with concurrent allocations for a different inode
+ * in the same block group.   The PID is used here so that functionally related
+ * files will be close-by on-disk.
+ *
+ *     Caller must make sure that @ind is valid and will stay that way.
+ */
+static ext4_fsblk_t ext4_find_near(struct inode *inode, Indirect *ind)
+{
+       struct ext4_inode_info *ei = EXT4_I(inode);
+       __le32 *start = ind->bh ? (__le32 *) ind->bh->b_data : ei->i_data;
+       __le32 *p;
+
+       /* Try to find previous block */
+       for (p = ind->p - 1; p >= start; p--) {
+               if (*p)
+                       return le32_to_cpu(*p);
+       }
+
+       /* No such thing, so let's try location of indirect block */
+       if (ind->bh)
+               return ind->bh->b_blocknr;
+
+       /*
+        * It is going to be referred to from the inode itself? OK, just put it
+        * into the same cylinder group then.
+        */
+       return ext4_inode_to_goal_block(inode);
+}
+
+/**
+ *     ext4_find_goal - find a preferred place for allocation.
+ *     @inode: owner
+ *     @block:  block we want
+ *     @partial: pointer to the last triple within a chain
+ *
+ *     Normally this function find the preferred place for block allocation,
+ *     returns it.
+ *     Because this is only used for non-extent files, we limit the block nr
+ *     to 32 bits.
+ */
+static ext4_fsblk_t ext4_find_goal(struct inode *inode, ext4_lblk_t block,
+                                  Indirect *partial)
+{
+       ext4_fsblk_t goal;
+
+       /*
+        * XXX need to get goal block from mballoc's data structures
+        */
+
+       goal = ext4_find_near(inode, partial);
+       goal = goal & EXT4_MAX_BLOCK_FILE_PHYS;
+       return goal;
+}
+
+/**
+ *     ext4_blks_to_allocate - Look up the block map and count the number
+ *     of direct blocks need to be allocated for the given branch.
+ *
+ *     @branch: chain of indirect blocks
+ *     @k: number of blocks need for indirect blocks
+ *     @blks: number of data blocks to be mapped.
+ *     @blocks_to_boundary:  the offset in the indirect block
+ *
+ *     return the total number of blocks to be allocate, including the
+ *     direct and indirect blocks.
+ */
+static int ext4_blks_to_allocate(Indirect *branch, int k, unsigned int blks,
+                                int blocks_to_boundary)
+{
+       unsigned int count = 0;
+
+       /*
+        * Simple case, [t,d]Indirect block(s) has not allocated yet
+        * then it's clear blocks on that path have not allocated
+        */
+       if (k > 0) {
+               /* right now we don't handle cross boundary allocation */
+               if (blks < blocks_to_boundary + 1)
+                       count += blks;
+               else
+                       count += blocks_to_boundary + 1;
+               return count;
+       }
+
+       count++;
+       while (count < blks && count <= blocks_to_boundary &&
+               le32_to_cpu(*(branch[0].p + count)) == 0) {
+               count++;
+       }
+       return count;
+}
+
+/**
+ *     ext4_alloc_blocks: multiple allocate blocks needed for a branch
+ *     @handle: handle for this transaction
+ *     @inode: inode which needs allocated blocks
+ *     @iblock: the logical block to start allocated at
+ *     @goal: preferred physical block of allocation
+ *     @indirect_blks: the number of blocks need to allocate for indirect
+ *                     blocks
+ *     @blks: number of desired blocks
+ *     @new_blocks: on return it will store the new block numbers for
+ *     the indirect blocks(if needed) and the first direct block,
+ *     @err: on return it will store the error code
+ *
+ *     This function will return the number of blocks allocated as
+ *     requested by the passed-in parameters.
+ */
+static int ext4_alloc_blocks(handle_t *handle, struct inode *inode,
+                            ext4_lblk_t iblock, ext4_fsblk_t goal,
+                            int indirect_blks, int blks,
+                            ext4_fsblk_t new_blocks[4], int *err)
+{
+       struct ext4_allocation_request ar;
+       int target, i;
+       unsigned long count = 0, blk_allocated = 0;
+       int index = 0;
+       ext4_fsblk_t current_block = 0;
+       int ret = 0;
+
+       /*
+        * Here we try to allocate the requested multiple blocks at once,
+        * on a best-effort basis.
+        * To build a branch, we should allocate blocks for
+        * the indirect blocks(if not allocated yet), and at least
+        * the first direct block of this branch.  That's the
+        * minimum number of blocks need to allocate(required)
+        */
+       /* first we try to allocate the indirect blocks */
+       target = indirect_blks;
+       while (target > 0) {
+               count = target;
+               /* allocating blocks for indirect blocks and direct blocks */
+               current_block = ext4_new_meta_blocks(handle, inode, goal,
+                                                    0, &count, err);
+               if (*err)
+                       goto failed_out;
+
+               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 */
+               while (index < indirect_blks && count) {
+                       new_blocks[index++] = current_block++;
+                       count--;
+               }
+               if (count > 0) {
+                       /*
+                        * save the new block number
+                        * for the first direct block
+                        */
+                       new_blocks[index] = current_block;
+                       printk(KERN_INFO "%s returned more blocks than "
+                                               "requested\n", __func__);
+                       WARN_ON(1);
+                       break;
+               }
+       }
+
+       target = blks - count ;
+       blk_allocated = count;
+       if (!target)
+               goto allocated;
+       /* Now allocate data blocks */
+       memset(&ar, 0, sizeof(ar));
+       ar.inode = inode;
+       ar.goal = goal;
+       ar.len = target;
+       ar.logical = iblock;
+       if (S_ISREG(inode->i_mode))
+               /* enable in-core preallocation only for regular files */
+               ar.flags = EXT4_MB_HINT_DATA;
+
+       current_block = ext4_mb_new_blocks(handle, &ar, err);
+       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)) {
+               /*
+                * if the allocation failed and we didn't allocate
+                * any blocks before
+                */
+               goto failed_out;
+       }
+       if (!*err) {
+               if (target == blks) {
+                       /*
+                        * save the new block number
+                        * for the first direct block
+                        */
+                       new_blocks[index] = current_block;
+               }
+               blk_allocated += ar.len;
+       }
+allocated:
+       /* total number of blocks allocated for direct blocks */
+       ret = blk_allocated;
+       *err = 0;
+       return ret;
+failed_out:
+       for (i = 0; i < index; i++)
+               ext4_free_blocks(handle, inode, NULL, new_blocks[i], 1, 0);
+       return ret;
+}
+
+/**
+ *     ext4_alloc_branch - allocate and set up a chain of blocks.
+ *     @handle: handle for this transaction
+ *     @inode: owner
+ *     @indirect_blks: number of allocated indirect blocks
+ *     @blks: number of allocated direct blocks
+ *     @goal: preferred place for allocation
+ *     @offsets: offsets (in the blocks) to store the pointers to next.
+ *     @branch: place to store the chain in.
+ *
+ *     This function allocates blocks, zeroes out all but the last one,
+ *     links them into chain and (if we are synchronous) writes them to disk.
+ *     In other words, it prepares a branch that can be spliced onto the
+ *     inode. It stores the information about that chain in the branch[], in
+ *     the same format as ext4_get_branch() would do. We are calling it after
+ *     we had read the existing part of chain and partial points to the last
+ *     triple of that (one with zero ->key). Upon the exit we have the same
+ *     picture as after the successful ext4_get_block(), except that in one
+ *     place chain is disconnected - *branch->p is still zero (we did not
+ *     set the last link), but branch->key contains the number that should
+ *     be placed into *branch->p to fill that gap.
+ *
+ *     If allocation fails we free all blocks we've allocated (and forget
+ *     their buffer_heads) and return the error value the from failed
+ *     ext4_alloc_block() (normally -ENOSPC). Otherwise we set the chain
+ *     as described above and return 0.
+ */
+static int ext4_alloc_branch(handle_t *handle, struct inode *inode,
+                            ext4_lblk_t iblock, int indirect_blks,
+                            int *blks, ext4_fsblk_t goal,
+                            ext4_lblk_t *offsets, Indirect *branch)
+{
+       int blocksize = inode->i_sb->s_blocksize;
+       int i, n = 0;
+       int err = 0;
+       struct buffer_head *bh;
+       int num;
+       ext4_fsblk_t new_blocks[4];
+       ext4_fsblk_t current_block;
+
+       num = ext4_alloc_blocks(handle, inode, iblock, goal, indirect_blks,
+                               *blks, new_blocks, &err);
+       if (err)
+               return err;
+
+       branch[0].key = cpu_to_le32(new_blocks[0]);
+       /*
+        * metadata blocks and data blocks are allocated.
+        */
+       for (n = 1; n <= indirect_blks;  n++) {
+               /*
+                * Get buffer_head for parent block, zero it out
+                * and set the pointer to new one, then send
+                * parent to disk.
+                */
+               bh = sb_getblk(inode->i_sb, new_blocks[n-1]);
+               if (unlikely(!bh)) {
+                       err = -EIO;
+                       goto failed;
+               }
+
+               branch[n].bh = bh;
+               lock_buffer(bh);
+               BUFFER_TRACE(bh, "call get_create_access");
+               err = ext4_journal_get_create_access(handle, bh);
+               if (err) {
+                       /* Don't brelse(bh) here; it's done in
+                        * ext4_journal_forget() below */
+                       unlock_buffer(bh);
+                       goto failed;
+               }
+
+               memset(bh->b_data, 0, blocksize);
+               branch[n].p = (__le32 *) bh->b_data + offsets[n];
+               branch[n].key = cpu_to_le32(new_blocks[n]);
+               *branch[n].p = branch[n].key;
+               if (n == indirect_blks) {
+                       current_block = new_blocks[n];
+                       /*
+                        * End of chain, update the last new metablock of
+                        * the chain to point to the new allocated
+                        * data blocks numbers
+                        */
+                       for (i = 1; i < num; i++)
+                               *(branch[n].p + i) = cpu_to_le32(++current_block);
+               }
+               BUFFER_TRACE(bh, "marking uptodate");
+               set_buffer_uptodate(bh);
+               unlock_buffer(bh);
+
+               BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
+               err = ext4_handle_dirty_metadata(handle, inode, bh);
+               if (err)
+                       goto failed;
+       }
+       *blks = num;
+       return err;
+failed:
+       /* Allocation failed, free what we already allocated */
+       ext4_free_blocks(handle, inode, NULL, new_blocks[0], 1, 0);
+       for (i = 1; i <= n ; i++) {
+               /*
+                * branch[i].bh is newly allocated, so there is no
+                * need to revoke the block, which is why we don't
+                * need to set EXT4_FREE_BLOCKS_METADATA.
+                */
+               ext4_free_blocks(handle, inode, NULL, new_blocks[i], 1,
+                                EXT4_FREE_BLOCKS_FORGET);
+       }
+       for (i = n+1; i < indirect_blks; i++)
+               ext4_free_blocks(handle, inode, NULL, new_blocks[i], 1, 0);
+
+       ext4_free_blocks(handle, inode, NULL, new_blocks[i], num, 0);
+
+       return err;
+}
+
+/**
+ * ext4_splice_branch - splice the allocated branch onto inode.
+ * @handle: handle for this transaction
+ * @inode: owner
+ * @block: (logical) number of block we are adding
+ * @chain: chain of indirect blocks (with a missing link - see
+ *     ext4_alloc_branch)
+ * @where: location of missing link
+ * @num:   number of indirect blocks we are adding
+ * @blks:  number of direct blocks we are adding
+ *
+ * This function fills the missing link and does all housekeeping needed in
+ * inode (->i_blocks, etc.). In case of success we end up with the full
+ * chain to new block and return 0.
+ */
+static int ext4_splice_branch(handle_t *handle, struct inode *inode,
+                             ext4_lblk_t block, Indirect *where, int num,
+                             int blks)
+{
+       int i;
+       int err = 0;
+       ext4_fsblk_t current_block;
+
+       /*
+        * If we're splicing into a [td]indirect block (as opposed to the
+        * inode) then we need to get write access to the [td]indirect block
+        * before the splice.
+        */
+       if (where->bh) {
+               BUFFER_TRACE(where->bh, "get_write_access");
+               err = ext4_journal_get_write_access(handle, where->bh);
+               if (err)
+                       goto err_out;
+       }
+       /* That's it */
+
+       *where->p = where->key;
+
+       /*
+        * Update the host buffer_head or inode to point to more just allocated
+        * direct blocks blocks
+        */
+       if (num == 0 && blks > 1) {
+               current_block = le32_to_cpu(where->key) + 1;
+               for (i = 1; i < blks; i++)
+                       *(where->p + i) = cpu_to_le32(current_block++);
+       }
+
+       /* We are done with atomic stuff, now do the rest of housekeeping */
+       /* had we spliced it onto indirect block? */
+       if (where->bh) {
+               /*
+                * If we spliced it onto an indirect block, we haven't
+                * altered the inode.  Note however that if it is being spliced
+                * onto an indirect block at the very end of the file (the
+                * file is growing) then we *will* alter the inode to reflect
+                * the new i_size.  But that is not done here - it is done in
+                * generic_commit_write->__mark_inode_dirty->ext4_dirty_inode.
+                */
+               jbd_debug(5, "splicing indirect only\n");
+               BUFFER_TRACE(where->bh, "call ext4_handle_dirty_metadata");
+               err = ext4_handle_dirty_metadata(handle, inode, where->bh);
+               if (err)
+                       goto err_out;
+       } else {
+               /*
+                * OK, we spliced it into the inode itself on a direct block.
+                */
+               ext4_mark_inode_dirty(handle, inode);
+               jbd_debug(5, "splicing direct\n");
+       }
+       return err;
+
+err_out:
+       for (i = 1; i <= num; i++) {
+               /*
+                * branch[i].bh is newly allocated, so there is no
+                * need to revoke the block, which is why we don't
+                * need to set EXT4_FREE_BLOCKS_METADATA.
+                */
+               ext4_free_blocks(handle, inode, where[i].bh, 0, 1,
+                                EXT4_FREE_BLOCKS_FORGET);
+       }
+       ext4_free_blocks(handle, inode, NULL, le32_to_cpu(where[num].key),
+                        blks, 0);
+
+       return err;
+}
+
+/*
+ * The ext4_ind_map_blocks() function handles non-extents inodes
+ * (i.e., using the traditional indirect/double-indirect i_blocks
+ * scheme) for ext4_map_blocks().
+ *
+ * Allocation strategy is simple: if we have to allocate something, we will
+ * have to go the whole way to leaf. So let's do it before attaching anything
+ * to tree, set linkage between the newborn blocks, write them if sync is
+ * required, recheck the path, free and repeat if check fails, otherwise
+ * set the last missing link (that will protect us from any truncate-generated
+ * removals - all blocks on the path are immune now) and possibly force the
+ * write on the parent block.
+ * That has a nice additional property: no special recovery from the failed
+ * allocations is needed - we simply release blocks and do not touch anything
+ * reachable from inode.
+ *
+ * `handle' can be NULL if create == 0.
+ *
+ * return > 0, # of blocks mapped or allocated.
+ * return = 0, if plain lookup failed.
+ * return < 0, error case.
+ *
+ * The ext4_ind_get_blocks() function should be called with
+ * down_write(&EXT4_I(inode)->i_data_sem) if allocating filesystem
+ * blocks (i.e., flags has EXT4_GET_BLOCKS_CREATE set) or
+ * down_read(&EXT4_I(inode)->i_data_sem) if not allocating file system
+ * blocks.
+ */
+int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
+                       struct ext4_map_blocks *map,
+                       int flags)
+{
+       int err = -EIO;
+       ext4_lblk_t offsets[4];
+       Indirect chain[4];
+       Indirect *partial;
+       ext4_fsblk_t goal;
+       int indirect_blks;
+       int blocks_to_boundary = 0;
+       int depth;
+       int count = 0;
+       ext4_fsblk_t first_block = 0;
+
+       trace_ext4_ind_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
+       J_ASSERT(!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)));
+       J_ASSERT(handle != NULL || (flags & EXT4_GET_BLOCKS_CREATE) == 0);
+       depth = ext4_block_to_path(inode, map->m_lblk, offsets,
+                                  &blocks_to_boundary);
+
+       if (depth == 0)
+               goto out;
+
+       partial = ext4_get_branch(inode, depth, offsets, chain, &err);
+
+       /* Simplest case - block found, no allocation needed */
+       if (!partial) {
+               first_block = le32_to_cpu(chain[depth - 1].key);
+               count++;
+               /*map more blocks*/
+               while (count < map->m_len && count <= blocks_to_boundary) {
+                       ext4_fsblk_t blk;
+
+                       blk = le32_to_cpu(*(chain[depth-1].p + count));
+
+                       if (blk == first_block + count)
+                               count++;
+                       else
+                               break;
+               }
+               goto got_it;
+       }
+
+       /* Next simple case - plain lookup or failed read of indirect block */
+       if ((flags & EXT4_GET_BLOCKS_CREATE) == 0 || err == -EIO)
+               goto cleanup;
+
+       /*
+        * Okay, we need to do block allocation.
+       */
+       goal = ext4_find_goal(inode, map->m_lblk, partial);
+
+       /* the number of blocks need to allocate for [d,t]indirect blocks */
+       indirect_blks = (chain + depth) - partial - 1;
+
+       /*
+        * Next look up the indirect map to count the totoal number of
+        * direct blocks to allocate for this branch.
+        */
+       count = ext4_blks_to_allocate(partial, indirect_blks,
+                                     map->m_len, blocks_to_boundary);
+       /*
+        * Block out ext4_truncate while we alter the tree
+        */
+       err = ext4_alloc_branch(handle, inode, map->m_lblk, indirect_blks,
+                               &count, goal,
+                               offsets + (partial - chain), partial);
+
+       /*
+        * The ext4_splice_branch call will free and forget any buffers
+        * on the new chain if there is a failure, but that risks using
+        * up transaction credits, especially for bitmaps where the
+        * credits cannot be returned.  Can we handle this somehow?  We
+        * may need to return -EAGAIN upwards in the worst case.  --sct
+        */
+       if (!err)
+               err = ext4_splice_branch(handle, inode, map->m_lblk,
+                                        partial, indirect_blks, count);
+       if (err)
+               goto cleanup;
+
+       map->m_flags |= EXT4_MAP_NEW;
+
+       ext4_update_inode_fsync_trans(handle, inode, 1);
+got_it:
+       map->m_flags |= EXT4_MAP_MAPPED;
+       map->m_pblk = le32_to_cpu(chain[depth-1].key);
+       map->m_len = count;
+       if (count > blocks_to_boundary)
+               map->m_flags |= EXT4_MAP_BOUNDARY;
+       err = count;
+       /* Clean up and exit */
+       partial = chain + depth - 1;    /* the whole chain */
+cleanup:
+       while (partial > chain) {
+               BUFFER_TRACE(partial->bh, "call brelse");
+               brelse(partial->bh);
+               partial--;
+       }
+out:
+       trace_ext4_ind_map_blocks_exit(inode, map->m_lblk,
+                               map->m_pblk, map->m_len, err);
+       return err;
+}
+
+/*
+ * O_DIRECT for ext3 (or indirect map) based files
+ *
+ * If the O_DIRECT write will extend the file then add this inode to the
+ * orphan list.  So recovery will truncate it back to the original size
+ * if the machine crashes during the write.
+ *
+ * If the O_DIRECT write is intantiating holes inside i_size and the machine
+ * crashes then stale disk data _may_ be exposed inside the file. But current
+ * VFS code falls back into buffered path in that case so we are safe.
+ */
+ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb,
+                          const struct iovec *iov, loff_t offset,
+                          unsigned long nr_segs)
+{
+       struct file *file = iocb->ki_filp;
+       struct inode *inode = file->f_mapping->host;
+       struct ext4_inode_info *ei = EXT4_I(inode);
+       handle_t *handle;
+       ssize_t ret;
+       int orphan = 0;
+       size_t count = iov_length(iov, nr_segs);
+       int retries = 0;
+
+       if (rw == WRITE) {
+               loff_t final_size = offset + count;
+
+               if (final_size > inode->i_size) {
+                       /* Credits for sb + inode write */
+                       handle = ext4_journal_start(inode, 2);
+                       if (IS_ERR(handle)) {
+                               ret = PTR_ERR(handle);
+                               goto out;
+                       }
+                       ret = ext4_orphan_add(handle, inode);
+                       if (ret) {
+                               ext4_journal_stop(handle);
+                               goto out;
+                       }
+                       orphan = 1;
+                       ei->i_disksize = inode->i_size;
+                       ext4_journal_stop(handle);
+               }
+       }
+
+retry:
+       if (rw == READ && ext4_should_dioread_nolock(inode))
+               ret = __blockdev_direct_IO(rw, iocb, inode,
+                                inode->i_sb->s_bdev, iov,
+                                offset, nr_segs,
+                                ext4_get_block, NULL, NULL, 0);
+       else {
+               ret = blockdev_direct_IO(rw, iocb, inode, iov,
+                                offset, nr_segs, ext4_get_block);
+
+               if (unlikely((rw & WRITE) && ret < 0)) {
+                       loff_t isize = i_size_read(inode);
+                       loff_t end = offset + iov_length(iov, nr_segs);
+
+                       if (end > isize)
+                               ext4_truncate_failed_write(inode);
+               }
+       }
+       if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
+               goto retry;
+
+       if (orphan) {
+               int err;
+
+               /* Credits for sb + inode write */
+               handle = ext4_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... */
+                       ret = PTR_ERR(handle);
+                       if (inode->i_nlink)
+                               ext4_orphan_del(NULL, inode);
+
+                       goto out;
+               }
+               if (inode->i_nlink)
+                       ext4_orphan_del(handle, inode);
+               if (ret > 0) {
+                       loff_t end = offset + ret;
+                       if (end > inode->i_size) {
+                               ei->i_disksize = end;
+                               i_size_write(inode, end);
+                               /*
+                                * We're going to return a positive `ret'
+                                * here due to non-zero-length I/O, so there's
+                                * no way of reporting error returns from
+                                * ext4_mark_inode_dirty() to userspace.  So
+                                * ignore it.
+                                */
+                               ext4_mark_inode_dirty(handle, inode);
+                       }
+               }
+               err = ext4_journal_stop(handle);
+               if (ret == 0)
+                       ret = err;
+       }
+out:
+       return ret;
+}
+
+/*
+ * Calculate the number of metadata blocks need to reserve
+ * to allocate a new block at @lblocks for non extent file based file
+ */
+int ext4_ind_calc_metadata_amount(struct inode *inode, sector_t lblock)
+{
+       struct ext4_inode_info *ei = EXT4_I(inode);
+       sector_t dind_mask = ~((sector_t)EXT4_ADDR_PER_BLOCK(inode->i_sb) - 1);
+       int blk_bits;
+
+       if (lblock < EXT4_NDIR_BLOCKS)
+               return 0;
+
+       lblock -= EXT4_NDIR_BLOCKS;
+
+       if (ei->i_da_metadata_calc_len &&
+           (lblock & dind_mask) == ei->i_da_metadata_calc_last_lblock) {
+               ei->i_da_metadata_calc_len++;
+               return 0;
+       }
+       ei->i_da_metadata_calc_last_lblock = lblock & dind_mask;
+       ei->i_da_metadata_calc_len = 1;
+       blk_bits = order_base_2(lblock);
+       return (blk_bits / EXT4_ADDR_PER_BLOCK_BITS(inode->i_sb)) + 1;
+}
+
+int ext4_ind_trans_blocks(struct inode *inode, int nrblocks, int chunk)
+{
+       int indirects;
+
+       /* if nrblocks are contiguous */
+       if (chunk) {
+               /*
+                * With N contiguous data blocks, we need at most
+                * N/EXT4_ADDR_PER_BLOCK(inode->i_sb) + 1 indirect blocks,
+                * 2 dindirect blocks, and 1 tindirect block
+                */
+               return DIV_ROUND_UP(nrblocks,
+                                   EXT4_ADDR_PER_BLOCK(inode->i_sb)) + 4;
+       }
+       /*
+        * if nrblocks are not contiguous, worse case, each block touch
+        * a indirect block, and each indirect block touch a double indirect
+        * block, plus a triple indirect block
+        */
+       indirects = nrblocks * 2 + 1;
+       return indirects;
+}
+
+/*
+ * Truncate transactions can be complex and absolutely huge.  So we need to
+ * be able to restart the transaction at a conventient checkpoint to make
+ * sure we don't overflow the journal.
+ *
+ * start_transaction gets us a new handle for a truncate transaction,
+ * and extend_transaction tries to extend the existing one a bit.  If
+ * extend fails, we need to propagate the failure up and restart the
+ * transaction in the top-level truncate loop. --sct
+ */
+static handle_t *start_transaction(struct inode *inode)
+{
+       handle_t *result;
+
+       result = ext4_journal_start(inode, ext4_blocks_for_truncate(inode));
+       if (!IS_ERR(result))
+               return result;
+
+       ext4_std_error(inode->i_sb, PTR_ERR(result));
+       return result;
+}
+
+/*
+ * Try to extend this transaction for the purposes of truncation.
+ *
+ * Returns 0 if we managed to create more room.  If we can't create more
+ * room, and the transaction must be restarted we return 1.
+ */
+static int try_to_extend_transaction(handle_t *handle, struct inode *inode)
+{
+       if (!ext4_handle_valid(handle))
+               return 0;
+       if (ext4_handle_has_enough_credits(handle, EXT4_RESERVE_TRANS_BLOCKS+1))
+               return 0;
+       if (!ext4_journal_extend(handle, ext4_blocks_for_truncate(inode)))
+               return 0;
+       return 1;
+}
+
+/*
+ * Probably it should be a library function... search for first non-zero word
+ * or memcmp with zero_page, whatever is better for particular architecture.
+ * Linus?
+ */
+static inline int all_zeroes(__le32 *p, __le32 *q)
+{
+       while (p < q)
+               if (*p++)
+                       return 0;
+       return 1;
+}
+
+/**
+ *     ext4_find_shared - find the indirect blocks for partial truncation.
+ *     @inode:   inode in question
+ *     @depth:   depth of the affected branch
+ *     @offsets: offsets of pointers in that branch (see ext4_block_to_path)
+ *     @chain:   place to store the pointers to partial indirect blocks
+ *     @top:     place to the (detached) top of branch
+ *
+ *     This is a helper function used by ext4_truncate().
+ *
+ *     When we do truncate() we may have to clean the ends of several
+ *     indirect blocks but leave the blocks themselves alive. Block is
+ *     partially truncated if some data below the new i_size is referred
+ *     from it (and it is on the path to the first completely truncated
+ *     data block, indeed).  We have to free the top of that path along
+ *     with everything to the right of the path. Since no allocation
+ *     past the truncation point is possible until ext4_truncate()
+ *     finishes, we may safely do the latter, but top of branch may
+ *     require special attention - pageout below the truncation point
+ *     might try to populate it.
+ *
+ *     We atomically detach the top of branch from the tree, store the
+ *     block number of its root in *@top, pointers to buffer_heads of
+ *     partially truncated blocks - in @chain[].bh and pointers to
+ *     their last elements that should not be removed - in
+ *     @chain[].p. Return value is the pointer to last filled element
+ *     of @chain.
+ *
+ *     The work left to caller to do the actual freeing of subtrees:
+ *             a) free the subtree starting from *@top
+ *             b) free the subtrees whose roots are stored in
+ *                     (@chain[i].p+1 .. end of @chain[i].bh->b_data)
+ *             c) free the subtrees growing from the inode past the @chain[0].
+ *                     (no partially truncated stuff there).  */
+
+static Indirect *ext4_find_shared(struct inode *inode, int depth,
+                                 ext4_lblk_t offsets[4], Indirect chain[4],
+                                 __le32 *top)
+{
+       Indirect *partial, *p;
+       int k, err;
+
+       *top = 0;
+       /* Make k index the deepest non-null offset + 1 */
+       for (k = depth; k > 1 && !offsets[k-1]; k--)
+               ;
+       partial = ext4_get_branch(inode, k, offsets, chain, &err);
+       /* Writer: pointers */
+       if (!partial)
+               partial = chain + k-1;
+       /*
+        * If the branch acquired continuation since we've looked at it -
+        * fine, it should all survive and (new) top doesn't belong to us.
+        */
+       if (!partial->key && *partial->p)
+               /* Writer: end */
+               goto no_top;
+       for (p = partial; (p > chain) && all_zeroes((__le32 *) p->bh->b_data, p->p); p--)
+               ;
+       /*
+        * OK, we've found the last block that must survive. The rest of our
+        * branch should be detached before unlocking. However, if that rest
+        * of branch is all ours and does not grow immediately from the inode
+        * it's easier to cheat and just decrement partial->p.
+        */
+       if (p == chain + k - 1 && p > chain) {
+               p->p--;
+       } else {
+               *top = *p->p;
+               /* Nope, don't do this in ext4.  Must leave the tree intact */
+#if 0
+               *p->p = 0;
+#endif
+       }
+       /* Writer: end */
+
+       while (partial > p) {
+               brelse(partial->bh);
+               partial--;
+       }
+no_top:
+       return partial;
+}
+
+/*
+ * Zero a number of block pointers in either an inode or an indirect block.
+ * If we restart the transaction we must again get write access to the
+ * indirect block for further modification.
+ *
+ * We release `count' blocks on disk, but (last - first) may be greater
+ * than `count' because there can be holes in there.
+ *
+ * Return 0 on success, 1 on invalid block range
+ * and < 0 on fatal error.
+ */
+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 | EXT4_FREE_BLOCKS_VALIDATED;
+       int     err;
+
+       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(inode, "attempt to clear invalid "
+                                "blocks %llu len %lu",
+                                (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");
+                       err = ext4_handle_dirty_metadata(handle, inode, bh);
+                       if (unlikely(err))
+                               goto out_err;
+               }
+               err = ext4_mark_inode_dirty(handle, inode);
+               if (unlikely(err))
+                       goto out_err;
+               err = ext4_truncate_restart_trans(handle, inode,
+                                       ext4_blocks_for_truncate(inode));
+               if (unlikely(err))
+                       goto out_err;
+               if (bh) {
+                       BUFFER_TRACE(bh, "retaking write access");
+                       err = ext4_journal_get_write_access(handle, bh);
+                       if (unlikely(err))
+                               goto out_err;
+               }
+       }
+
+       for (p = first; p < last; p++)
+               *p = 0;
+
+       ext4_free_blocks(handle, inode, NULL, block_to_free, count, flags);
+       return 0;
+out_err:
+       ext4_std_error(inode->i_sb, err);
+       return err;
+}
+
+/**
+ * ext4_free_data - free a list of data blocks
+ * @handle:    handle for this transaction
+ * @inode:     inode we are dealing with
+ * @this_bh:   indirect buffer_head which contains *@first and *@last
+ * @first:     array of block numbers
+ * @last:      points immediately past the end of array
+ *
+ * We are freeing all blocks referred from that array (numbers are stored as
+ * little-endian 32-bit) and updating @inode->i_blocks appropriately.
+ *
+ * We accumulate contiguous runs of blocks to free.  Conveniently, if these
+ * blocks are contiguous then releasing them at one time will only affect one
+ * or two bitmap blocks (+ group descriptor(s) and superblock) and we won't
+ * actually use a lot of journal space.
+ *
+ * @this_bh will be %NULL if @first and @last point into the inode's direct
+ * block pointers.
+ */
+static void ext4_free_data(handle_t *handle, struct inode *inode,
+                          struct buffer_head *this_bh,
+                          __le32 *first, __le32 *last)
+{
+       ext4_fsblk_t block_to_free = 0;    /* Starting block # of a run */
+       unsigned long count = 0;            /* Number of blocks in the run */
+       __le32 *block_to_free_p = NULL;     /* Pointer into inode/ind
+                                              corresponding to
+                                              block_to_free */
+       ext4_fsblk_t nr;                    /* Current block # */
+       __le32 *p;                          /* Pointer into inode/ind
+                                              for current block */
+       int err = 0;
+
+       if (this_bh) {                          /* For indirect block */
+               BUFFER_TRACE(this_bh, "get_write_access");
+               err = ext4_journal_get_write_access(handle, this_bh);
+               /* Important: if we can't update the indirect pointers
+                * to the blocks, we can't free them. */
+               if (err)
+                       return;
+       }
+
+       for (p = first; p < last; p++) {
+               nr = le32_to_cpu(*p);
+               if (nr) {
+                       /* accumulate blocks to free if they're contiguous */
+                       if (count == 0) {
+                               block_to_free = nr;
+                               block_to_free_p = p;
+                               count = 1;
+                       } else if (nr == block_to_free + count) {
+                               count++;
+                       } else {
+                               err = ext4_clear_blocks(handle, inode, this_bh,
+                                                       block_to_free, count,
+                                                       block_to_free_p, p);
+                               if (err)
+                                       break;
+                               block_to_free = nr;
+                               block_to_free_p = p;
+                               count = 1;
+                       }
+               }
+       }
+
+       if (!err && count > 0)
+               err = ext4_clear_blocks(handle, inode, this_bh, block_to_free,
+                                       count, block_to_free_p, p);
+       if (err < 0)
+               /* fatal error */
+               return;
+
+       if (this_bh) {
+               BUFFER_TRACE(this_bh, "call ext4_handle_dirty_metadata");
+
+               /*
+                * The buffer head should have an attached journal head at this
+                * point. However, if the data is corrupted and an indirect
+                * block pointed to itself, it would have been detached when
+                * the block was cleared. Check for this instead of OOPSing.
+                */
+               if ((EXT4_JOURNAL(inode) == NULL) || bh2jh(this_bh))
+                       ext4_handle_dirty_metadata(handle, inode, this_bh);
+               else
+                       EXT4_ERROR_INODE(inode,
+                                        "circular indirect block detected at "
+                                        "block %llu",
+                               (unsigned long long) this_bh->b_blocknr);
+       }
+}
+
+/**
+ *     ext4_free_branches - free an array of branches
+ *     @handle: JBD handle for this transaction
+ *     @inode: inode we are dealing with
+ *     @parent_bh: the buffer_head which contains *@first and *@last
+ *     @first: array of block numbers
+ *     @last:  pointer immediately past the end of array
+ *     @depth: depth of the branches to free
+ *
+ *     We are freeing all blocks referred from these branches (numbers are
+ *     stored as little-endian 32-bit) and updating @inode->i_blocks
+ *     appropriately.
+ */
+static void ext4_free_branches(handle_t *handle, struct inode *inode,
+                              struct buffer_head *parent_bh,
+                              __le32 *first, __le32 *last, int depth)
+{
+       ext4_fsblk_t nr;
+       __le32 *p;
+
+       if (ext4_handle_is_aborted(handle))
+               return;
+
+       if (depth--) {
+               struct buffer_head *bh;
+               int addr_per_block = EXT4_ADDR_PER_BLOCK(inode->i_sb);
+               p = last;
+               while (--p >= first) {
+                       nr = le32_to_cpu(*p);
+                       if (!nr)
+                               continue;               /* A hole */
+
+                       if (!ext4_data_block_valid(EXT4_SB(inode->i_sb),
+                                                  nr, 1)) {
+                               EXT4_ERROR_INODE(inode,
+                                                "invalid indirect mapped "
+                                                "block %lu (level %d)",
+                                                (unsigned long) nr, depth);
+                               break;
+                       }
+
+                       /* Go read the buffer for the next level down */
+                       bh = sb_bread(inode->i_sb, nr);
+
+                       /*
+                        * A read failure? Report error and clear slot
+                        * (should be rare).
+                        */
+                       if (!bh) {
+                               EXT4_ERROR_INODE_BLOCK(inode, nr,
+                                                      "Read failure");
+                               continue;
+                       }
+
+                       /* This zaps the entire block.  Bottom up. */
+                       BUFFER_TRACE(bh, "free child branches");
+                       ext4_free_branches(handle, inode, bh,
+                                       (__le32 *) bh->b_data,
+                                       (__le32 *) bh->b_data + addr_per_block,
+                                       depth);
+                       brelse(bh);
+
+                       /*
+                        * Everything below this this pointer has been
+                        * released.  Now let this top-of-subtree go.
+                        *
+                        * We want the freeing of this indirect block to be
+                        * atomic in the journal with the updating of the
+                        * bitmap block which owns it.  So make some room in
+                        * the journal.
+                        *
+                        * We zero the parent pointer *after* freeing its
+                        * pointee in the bitmaps, so if extend_transaction()
+                        * for some reason fails to put the bitmap changes and
+                        * the release into the same transaction, recovery
+                        * will merely complain about releasing a free block,
+                        * rather than leaking blocks.
+                        */
+                       if (ext4_handle_is_aborted(handle))
+                               return;
+                       if (try_to_extend_transaction(handle, inode)) {
+                               ext4_mark_inode_dirty(handle, inode);
+                               ext4_truncate_restart_trans(handle, inode,
+                                           ext4_blocks_for_truncate(inode));
+                       }
+
+                       /*
+                        * The forget flag here is critical because if
+                        * we are journaling (and not doing data
+                        * journaling), we have to make sure a revoke
+                        * record is written to prevent the journal
+                        * replay from overwriting the (former)
+                        * indirect block if it gets reallocated as a
+                        * data block.  This must happen in the same
+                        * transaction where the data blocks are
+                        * actually freed.
+                        */
+                       ext4_free_blocks(handle, inode, NULL, nr, 1,
+                                        EXT4_FREE_BLOCKS_METADATA|
+                                        EXT4_FREE_BLOCKS_FORGET);
+
+                       if (parent_bh) {
+                               /*
+                                * The block which we have just freed is
+                                * pointed to by an indirect block: journal it
+                                */
+                               BUFFER_TRACE(parent_bh, "get_write_access");
+                               if (!ext4_journal_get_write_access(handle,
+                                                                  parent_bh)){
+                                       *p = 0;
+                                       BUFFER_TRACE(parent_bh,
+                                       "call ext4_handle_dirty_metadata");
+                                       ext4_handle_dirty_metadata(handle,
+                                                                  inode,
+                                                                  parent_bh);
+                               }
+                       }
+               }
+       } else {
+               /* We have reached the bottom of the tree. */
+               BUFFER_TRACE(parent_bh, "free data blocks");
+               ext4_free_data(handle, inode, parent_bh, first, last);
+       }
+}
+
+void ext4_ind_truncate(struct inode *inode)
+{
+       handle_t *handle;
+       struct ext4_inode_info *ei = EXT4_I(inode);
+       __le32 *i_data = ei->i_data;
+       int addr_per_block = EXT4_ADDR_PER_BLOCK(inode->i_sb);
+       struct address_space *mapping = inode->i_mapping;
+       ext4_lblk_t offsets[4];
+       Indirect chain[4];
+       Indirect *partial;
+       __le32 nr = 0;
+       int n = 0;
+       ext4_lblk_t last_block, max_block;
+       unsigned blocksize = inode->i_sb->s_blocksize;
+
+       handle = start_transaction(inode);
+       if (IS_ERR(handle))
+               return;         /* AKPM: return what? */
+
+       last_block = (inode->i_size + blocksize-1)
+                                       >> EXT4_BLOCK_SIZE_BITS(inode->i_sb);
+       max_block = (EXT4_SB(inode->i_sb)->s_bitmap_maxbytes + blocksize-1)
+                                       >> EXT4_BLOCK_SIZE_BITS(inode->i_sb);
+
+       if (inode->i_size & (blocksize - 1))
+               if (ext4_block_truncate_page(handle, mapping, inode->i_size))
+                       goto out_stop;
+
+       if (last_block != max_block) {
+               n = ext4_block_to_path(inode, last_block, offsets, NULL);
+               if (n == 0)
+                       goto out_stop;  /* error */
+       }
+
+       /*
+        * OK.  This truncate is going to happen.  We add the inode to the
+        * orphan list, so that if this truncate spans multiple transactions,
+        * and we crash, we will resume the truncate when the filesystem
+        * recovers.  It also marks the inode dirty, to catch the new size.
+        *
+        * Implication: the file must always be in a sane, consistent
+        * truncatable state while each transaction commits.
+        */
+       if (ext4_orphan_add(handle, inode))
+               goto out_stop;
+
+       /*
+        * From here we block out all ext4_get_block() callers who want to
+        * modify the block allocation tree.
+        */
+       down_write(&ei->i_data_sem);
+
+       ext4_discard_preallocations(inode);
+
+       /*
+        * The orphan list entry will now protect us from any crash which
+        * occurs before the truncate completes, so it is now safe to propagate
+        * the new, shorter inode size (held for now in i_size) into the
+        * on-disk inode. We do this via i_disksize, which is the value which
+        * ext4 *really* writes onto the disk inode.
+        */
+       ei->i_disksize = inode->i_size;
+
+       if (last_block == max_block) {
+               /*
+                * It is unnecessary to free any data blocks if last_block is
+                * equal to the indirect block limit.
+                */
+               goto out_unlock;
+       } else if (n == 1) {            /* direct blocks */
+               ext4_free_data(handle, inode, NULL, i_data+offsets[0],
+                              i_data + EXT4_NDIR_BLOCKS);
+               goto do_indirects;
+       }
+
+       partial = ext4_find_shared(inode, n, offsets, chain, &nr);
+       /* Kill the top of shared branch (not detached) */
+       if (nr) {
+               if (partial == chain) {
+                       /* Shared branch grows from the inode */
+                       ext4_free_branches(handle, inode, NULL,
+                                          &nr, &nr+1, (chain+n-1) - partial);
+                       *partial->p = 0;
+                       /*
+                        * We mark the inode dirty prior to restart,
+                        * and prior to stop.  No need for it here.
+                        */
+               } else {
+                       /* Shared branch grows from an indirect block */
+                       BUFFER_TRACE(partial->bh, "get_write_access");
+                       ext4_free_branches(handle, inode, partial->bh,
+                                       partial->p,
+                                       partial->p+1, (chain+n-1) - partial);
+               }
+       }
+       /* Clear the ends of indirect blocks on the shared branch */
+       while (partial > chain) {
+               ext4_free_branches(handle, inode, partial->bh, partial->p + 1,
+                                  (__le32*)partial->bh->b_data+addr_per_block,
+                                  (chain+n-1) - partial);
+               BUFFER_TRACE(partial->bh, "call brelse");
+               brelse(partial->bh);
+               partial--;
+       }
+do_indirects:
+       /* Kill the remaining (whole) subtrees */
+       switch (offsets[0]) {
+       default:
+               nr = i_data[EXT4_IND_BLOCK];
+               if (nr) {
+                       ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 1);
+                       i_data[EXT4_IND_BLOCK] = 0;
+               }
+       case EXT4_IND_BLOCK:
+               nr = i_data[EXT4_DIND_BLOCK];
+               if (nr) {
+                       ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 2);
+                       i_data[EXT4_DIND_BLOCK] = 0;
+               }
+       case EXT4_DIND_BLOCK:
+               nr = i_data[EXT4_TIND_BLOCK];
+               if (nr) {
+                       ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 3);
+                       i_data[EXT4_TIND_BLOCK] = 0;
+               }
+       case EXT4_TIND_BLOCK:
+               ;
+       }
+
+out_unlock:
+       up_write(&ei->i_data_sem);
+       inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
+       ext4_mark_inode_dirty(handle, inode);
+
+       /*
+        * In a multi-transaction truncate, we only make the final transaction
+        * synchronous
+        */
+       if (IS_SYNC(inode))
+               ext4_handle_sync(handle);
+out_stop:
+       /*
+        * If this was a simple ftruncate(), and the file will remain alive
+        * then we need to clear up the orphan record which we created above.
+        * However, if this was a real unlink then we were called by
+        * ext4_delete_inode(), and we allow that function to clean up the
+        * orphan info for us.
+        */
+       if (inode->i_nlink)
+               ext4_orphan_del(handle, inode);
+
+       ext4_journal_stop(handle);
+       trace_ext4_truncate_exit(inode);
+}
+
index 3e5191f9f398e9b3c598c5b958612dd9740055b0..d47264cafee00e2f95e90460d67c42c2bc1d7656 100644 (file)
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
  *
- *  Goal-directed block allocation by Stephen Tweedie
- *     (sct@redhat.com), 1993, 1998
- *  Big-endian to little-endian byte-swapping/bitmaps by
- *        David S. Miller (davem@caip.rutgers.edu), 1995
  *  64-bit file support on 64-bit platforms by Jakub Jelinek
  *     (jj@sunsite.ms.mff.cuni.cz)
  *
@@ -47,6 +43,7 @@
 #include "xattr.h"
 #include "acl.h"
 #include "ext4_extents.h"
+#include "truncate.h"
 
 #include <trace/events/ext4.h>
 
@@ -88,72 +85,6 @@ static int ext4_inode_is_fast_symlink(struct inode *inode)
        return (S_ISLNK(inode->i_mode) && inode->i_blocks - ea_blocks == 0);
 }
 
-/*
- * Work out how many blocks we need to proceed with the next chunk of a
- * truncate transaction.
- */
-static unsigned long blocks_for_truncate(struct inode *inode)
-{
-       ext4_lblk_t needed;
-
-       needed = inode->i_blocks >> (inode->i_sb->s_blocksize_bits - 9);
-
-       /* Give ourselves just enough room to cope with inodes in which
-        * i_blocks is corrupt: we've seen disk corruptions in the past
-        * which resulted in random data in an inode which looked enough
-        * like a regular file for ext4 to try to delete it.  Things
-        * will go a bit crazy if that happens, but at least we should
-        * try not to panic the whole kernel. */
-       if (needed < 2)
-               needed = 2;
-
-       /* But we need to bound the transaction so we don't overflow the
-        * journal. */
-       if (needed > EXT4_MAX_TRANS_DATA)
-               needed = EXT4_MAX_TRANS_DATA;
-
-       return EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + needed;
-}
-
-/*
- * Truncate transactions can be complex and absolutely huge.  So we need to
- * be able to restart the transaction at a conventient checkpoint to make
- * sure we don't overflow the journal.
- *
- * start_transaction gets us a new handle for a truncate transaction,
- * and extend_transaction tries to extend the existing one a bit.  If
- * extend fails, we need to propagate the failure up and restart the
- * transaction in the top-level truncate loop. --sct
- */
-static handle_t *start_transaction(struct inode *inode)
-{
-       handle_t *result;
-
-       result = ext4_journal_start(inode, blocks_for_truncate(inode));
-       if (!IS_ERR(result))
-               return result;
-
-       ext4_std_error(inode->i_sb, PTR_ERR(result));
-       return result;
-}
-
-/*
- * Try to extend this transaction for the purposes of truncation.
- *
- * Returns 0 if we managed to create more room.  If we can't create more
- * room, and the transaction must be restarted we return 1.
- */
-static int try_to_extend_transaction(handle_t *handle, struct inode *inode)
-{
-       if (!ext4_handle_valid(handle))
-               return 0;
-       if (ext4_handle_has_enough_credits(handle, EXT4_RESERVE_TRANS_BLOCKS+1))
-               return 0;
-       if (!ext4_journal_extend(handle, blocks_for_truncate(inode)))
-               return 0;
-       return 1;
-}
-
 /*
  * Restart the transaction associated with *handle.  This does a commit,
  * so before we call here everything must be consistently dirtied against
@@ -190,6 +121,33 @@ void ext4_evict_inode(struct inode *inode)
 
        trace_ext4_evict_inode(inode);
        if (inode->i_nlink) {
+               /*
+                * When journalling data dirty buffers are tracked only in the
+                * journal. So although mm thinks everything is clean and
+                * ready for reaping the inode might still have some pages to
+                * write in the running transaction or waiting to be
+                * checkpointed. Thus calling jbd2_journal_invalidatepage()
+                * (via truncate_inode_pages()) to discard these buffers can
+                * cause data loss. Also even if we did not discard these
+                * buffers, we would have no way to find them after the inode
+                * is reaped and thus user could see stale data if he tries to
+                * read them before the transaction is checkpointed. So be
+                * careful and force everything to disk here... We use
+                * ei->i_datasync_tid to store the newest transaction
+                * containing inode's data.
+                *
+                * Note that directories do not have this problem because they
+                * don't use page cache.
+                */
+               if (ext4_should_journal_data(inode) &&
+                   (S_ISLNK(inode->i_mode) || S_ISREG(inode->i_mode))) {
+                       journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
+                       tid_t commit_tid = EXT4_I(inode)->i_datasync_tid;
+
+                       jbd2_log_start_commit(journal, commit_tid);
+                       jbd2_log_wait_commit(journal, commit_tid);
+                       filemap_write_and_wait(&inode->i_data);
+               }
                truncate_inode_pages(&inode->i_data, 0);
                goto no_delete;
        }
@@ -204,7 +162,7 @@ void ext4_evict_inode(struct inode *inode)
        if (is_bad_inode(inode))
                goto no_delete;
 
-       handle = ext4_journal_start(inode, blocks_for_truncate(inode)+3);
+       handle = ext4_journal_start(inode, ext4_blocks_for_truncate(inode)+3);
        if (IS_ERR(handle)) {
                ext4_std_error(inode->i_sb, PTR_ERR(handle));
                /*
@@ -277,793 +235,6 @@ no_delete:
        ext4_clear_inode(inode);        /* We must guarantee clearing of inode... */
 }
 
-typedef struct {
-       __le32  *p;
-       __le32  key;
-       struct buffer_head *bh;
-} Indirect;
-
-static inline void add_chain(Indirect *p, struct buffer_head *bh, __le32 *v)
-{
-       p->key = *(p->p = v);
-       p->bh = bh;
-}
-
-/**
- *     ext4_block_to_path - parse the block number into array of offsets
- *     @inode: inode in question (we are only interested in its superblock)
- *     @i_block: block number to be parsed
- *     @offsets: array to store the offsets in
- *     @boundary: set this non-zero if the referred-to block is likely to be
- *            followed (on disk) by an indirect block.
- *
- *     To store the locations of file's data ext4 uses a data structure common
- *     for UNIX filesystems - tree of pointers anchored in the inode, with
- *     data blocks at leaves and indirect blocks in intermediate nodes.
- *     This function translates the block number into path in that tree -
- *     return value is the path length and @offsets[n] is the offset of
- *     pointer to (n+1)th node in the nth one. If @block is out of range
- *     (negative or too large) warning is printed and zero returned.
- *
- *     Note: function doesn't find node addresses, so no IO is needed. All
- *     we need to know is the capacity of indirect blocks (taken from the
- *     inode->i_sb).
- */
-
-/*
- * Portability note: the last comparison (check that we fit into triple
- * indirect block) is spelled differently, because otherwise on an
- * architecture with 32-bit longs and 8Kb pages we might get into trouble
- * if our filesystem had 8Kb blocks. We might use long long, but that would
- * kill us on x86. Oh, well, at least the sign propagation does not matter -
- * i_block would have to be negative in the very beginning, so we would not
- * get there at all.
- */
-
-static int ext4_block_to_path(struct inode *inode,
-                             ext4_lblk_t i_block,
-                             ext4_lblk_t offsets[4], int *boundary)
-{
-       int ptrs = EXT4_ADDR_PER_BLOCK(inode->i_sb);
-       int ptrs_bits = EXT4_ADDR_PER_BLOCK_BITS(inode->i_sb);
-       const long direct_blocks = EXT4_NDIR_BLOCKS,
-               indirect_blocks = ptrs,
-               double_blocks = (1 << (ptrs_bits * 2));
-       int n = 0;
-       int final = 0;
-
-       if (i_block < direct_blocks) {
-               offsets[n++] = i_block;
-               final = direct_blocks;
-       } else if ((i_block -= direct_blocks) < indirect_blocks) {
-               offsets[n++] = EXT4_IND_BLOCK;
-               offsets[n++] = i_block;
-               final = ptrs;
-       } else if ((i_block -= indirect_blocks) < double_blocks) {
-               offsets[n++] = EXT4_DIND_BLOCK;
-               offsets[n++] = i_block >> ptrs_bits;
-               offsets[n++] = i_block & (ptrs - 1);
-               final = ptrs;
-       } else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
-               offsets[n++] = EXT4_TIND_BLOCK;
-               offsets[n++] = i_block >> (ptrs_bits * 2);
-               offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
-               offsets[n++] = i_block & (ptrs - 1);
-               final = ptrs;
-       } else {
-               ext4_warning(inode->i_sb, "block %lu > max in inode %lu",
-                            i_block + direct_blocks +
-                            indirect_blocks + double_blocks, inode->i_ino);
-       }
-       if (boundary)
-               *boundary = final - 1 - (i_block & (ptrs - 1));
-       return n;
-}
-
-static int __ext4_check_blockref(const char *function, unsigned int line,
-                                struct inode *inode,
-                                __le32 *p, unsigned int max)
-{
-       struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
-       __le32 *bref = p;
-       unsigned int blk;
-
-       while (bref < p+max) {
-               blk = le32_to_cpu(*bref++);
-               if (blk &&
-                   unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb),
-                                                   blk, 1))) {
-                       es->s_last_error_block = cpu_to_le64(blk);
-                       ext4_error_inode(inode, function, line, blk,
-                                        "invalid block");
-                       return -EIO;
-               }
-       }
-       return 0;
-}
-
-
-#define ext4_check_indirect_blockref(inode, bh)                         \
-       __ext4_check_blockref(__func__, __LINE__, inode,                \
-                             (__le32 *)(bh)->b_data,                   \
-                             EXT4_ADDR_PER_BLOCK((inode)->i_sb))
-
-#define ext4_check_inode_blockref(inode)                                \
-       __ext4_check_blockref(__func__, __LINE__, inode,                \
-                             EXT4_I(inode)->i_data,                    \
-                             EXT4_NDIR_BLOCKS)
-
-/**
- *     ext4_get_branch - read the chain of indirect blocks leading to data
- *     @inode: inode in question
- *     @depth: depth of the chain (1 - direct pointer, etc.)
- *     @offsets: offsets of pointers in inode/indirect blocks
- *     @chain: place to store the result
- *     @err: here we store the error value
- *
- *     Function fills the array of triples <key, p, bh> and returns %NULL
- *     if everything went OK or the pointer to the last filled triple
- *     (incomplete one) otherwise. Upon the return chain[i].key contains
- *     the number of (i+1)-th block in the chain (as it is stored in memory,
- *     i.e. little-endian 32-bit), chain[i].p contains the address of that
- *     number (it points into struct inode for i==0 and into the bh->b_data
- *     for i>0) and chain[i].bh points to the buffer_head of i-th indirect
- *     block for i>0 and NULL for i==0. In other words, it holds the block
- *     numbers of the chain, addresses they were taken from (and where we can
- *     verify that chain did not change) and buffer_heads hosting these
- *     numbers.
- *
- *     Function stops when it stumbles upon zero pointer (absent block)
- *             (pointer to last triple returned, *@err == 0)
- *     or when it gets an IO error reading an indirect block
- *             (ditto, *@err == -EIO)
- *     or when it reads all @depth-1 indirect blocks successfully and finds
- *     the whole chain, all way to the data (returns %NULL, *err == 0).
- *
- *      Need to be called with
- *      down_read(&EXT4_I(inode)->i_data_sem)
- */
-static Indirect *ext4_get_branch(struct inode *inode, int depth,
-                                ext4_lblk_t  *offsets,
-                                Indirect chain[4], int *err)
-{
-       struct super_block *sb = inode->i_sb;
-       Indirect *p = chain;
-       struct buffer_head *bh;
-
-       *err = 0;
-       /* i_data is not going away, no lock needed */
-       add_chain(chain, NULL, EXT4_I(inode)->i_data + *offsets);
-       if (!p->key)
-               goto no_block;
-       while (--depth) {
-               bh = sb_getblk(sb, le32_to_cpu(p->key));
-               if (unlikely(!bh))
-                       goto failure;
-
-               if (!bh_uptodate_or_lock(bh)) {
-                       if (bh_submit_read(bh) < 0) {
-                               put_bh(bh);
-                               goto failure;
-                       }
-                       /* validate block references */
-                       if (ext4_check_indirect_blockref(inode, bh)) {
-                               put_bh(bh);
-                               goto failure;
-                       }
-               }
-
-               add_chain(++p, bh, (__le32 *)bh->b_data + *++offsets);
-               /* Reader: end */
-               if (!p->key)
-                       goto no_block;
-       }
-       return NULL;
-
-failure:
-       *err = -EIO;
-no_block:
-       return p;
-}
-
-/**
- *     ext4_find_near - find a place for allocation with sufficient locality
- *     @inode: owner
- *     @ind: descriptor of indirect block.
- *
- *     This function returns the preferred place for block allocation.
- *     It is used when heuristic for sequential allocation fails.
- *     Rules are:
- *       + if there is a block to the left of our position - allocate near it.
- *       + if pointer will live in indirect block - allocate near that block.
- *       + if pointer will live in inode - allocate in the same
- *         cylinder group.
- *
- * In the latter case we colour the starting block by the callers PID to
- * prevent it from clashing with concurrent allocations for a different inode
- * in the same block group.   The PID is used here so that functionally related
- * files will be close-by on-disk.
- *
- *     Caller must make sure that @ind is valid and will stay that way.
- */
-static ext4_fsblk_t ext4_find_near(struct inode *inode, Indirect *ind)
-{
-       struct ext4_inode_info *ei = EXT4_I(inode);
-       __le32 *start = ind->bh ? (__le32 *) ind->bh->b_data : ei->i_data;
-       __le32 *p;
-       ext4_fsblk_t bg_start;
-       ext4_fsblk_t last_block;
-       ext4_grpblk_t colour;
-       ext4_group_t block_group;
-       int flex_size = ext4_flex_bg_size(EXT4_SB(inode->i_sb));
-
-       /* Try to find previous block */
-       for (p = ind->p - 1; p >= start; p--) {
-               if (*p)
-                       return le32_to_cpu(*p);
-       }
-
-       /* No such thing, so let's try location of indirect block */
-       if (ind->bh)
-               return ind->bh->b_blocknr;
-
-       /*
-        * It is going to be referred to from the inode itself? OK, just put it
-        * into the same cylinder group then.
-        */
-       block_group = ei->i_block_group;
-       if (flex_size >= EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME) {
-               block_group &= ~(flex_size-1);
-               if (S_ISREG(inode->i_mode))
-                       block_group++;
-       }
-       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;
-
-       /*
-        * If we are doing delayed allocation, we don't need take
-        * colour into account.
-        */
-       if (test_opt(inode->i_sb, DELALLOC))
-               return bg_start;
-
-       if (bg_start + EXT4_BLOCKS_PER_GROUP(inode->i_sb) <= last_block)
-               colour = (current->pid % 16) *
-                       (EXT4_BLOCKS_PER_GROUP(inode->i_sb) / 16);
-       else
-               colour = (current->pid % 16) * ((last_block - bg_start) / 16);
-       return bg_start + colour;
-}
-
-/**
- *     ext4_find_goal - find a preferred place for allocation.
- *     @inode: owner
- *     @block:  block we want
- *     @partial: pointer to the last triple within a chain
- *
- *     Normally this function find the preferred place for block allocation,
- *     returns it.
- *     Because this is only used for non-extent files, we limit the block nr
- *     to 32 bits.
- */
-static ext4_fsblk_t ext4_find_goal(struct inode *inode, ext4_lblk_t block,
-                                  Indirect *partial)
-{
-       ext4_fsblk_t goal;
-
-       /*
-        * XXX need to get goal block from mballoc's data structures
-        */
-
-       goal = ext4_find_near(inode, partial);
-       goal = goal & EXT4_MAX_BLOCK_FILE_PHYS;
-       return goal;
-}
-
-/**
- *     ext4_blks_to_allocate - Look up the block map and count the number
- *     of direct blocks need to be allocated for the given branch.
- *
- *     @branch: chain of indirect blocks
- *     @k: number of blocks need for indirect blocks
- *     @blks: number of data blocks to be mapped.
- *     @blocks_to_boundary:  the offset in the indirect block
- *
- *     return the total number of blocks to be allocate, including the
- *     direct and indirect blocks.
- */
-static int ext4_blks_to_allocate(Indirect *branch, int k, unsigned int blks,
-                                int blocks_to_boundary)
-{
-       unsigned int count = 0;
-
-       /*
-        * Simple case, [t,d]Indirect block(s) has not allocated yet
-        * then it's clear blocks on that path have not allocated
-        */
-       if (k > 0) {
-               /* right now we don't handle cross boundary allocation */
-               if (blks < blocks_to_boundary + 1)
-                       count += blks;
-               else
-                       count += blocks_to_boundary + 1;
-               return count;
-       }
-
-       count++;
-       while (count < blks && count <= blocks_to_boundary &&
-               le32_to_cpu(*(branch[0].p + count)) == 0) {
-               count++;
-       }
-       return count;
-}
-
-/**
- *     ext4_alloc_blocks: multiple allocate blocks needed for a branch
- *     @handle: handle for this transaction
- *     @inode: inode which needs allocated blocks
- *     @iblock: the logical block to start allocated at
- *     @goal: preferred physical block of allocation
- *     @indirect_blks: the number of blocks need to allocate for indirect
- *                     blocks
- *     @blks: number of desired blocks
- *     @new_blocks: on return it will store the new block numbers for
- *     the indirect blocks(if needed) and the first direct block,
- *     @err: on return it will store the error code
- *
- *     This function will return the number of blocks allocated as
- *     requested by the passed-in parameters.
- */
-static int ext4_alloc_blocks(handle_t *handle, struct inode *inode,
-                            ext4_lblk_t iblock, ext4_fsblk_t goal,
-                            int indirect_blks, int blks,
-                            ext4_fsblk_t new_blocks[4], int *err)
-{
-       struct ext4_allocation_request ar;
-       int target, i;
-       unsigned long count = 0, blk_allocated = 0;
-       int index = 0;
-       ext4_fsblk_t current_block = 0;
-       int ret = 0;
-
-       /*
-        * Here we try to allocate the requested multiple blocks at once,
-        * on a best-effort basis.
-        * To build a branch, we should allocate blocks for
-        * the indirect blocks(if not allocated yet), and at least
-        * the first direct block of this branch.  That's the
-        * minimum number of blocks need to allocate(required)
-        */
-       /* first we try to allocate the indirect blocks */
-       target = indirect_blks;
-       while (target > 0) {
-               count = target;
-               /* allocating blocks for indirect blocks and direct blocks */
-               current_block = ext4_new_meta_blocks(handle, inode, goal,
-                                                    0, &count, err);
-               if (*err)
-                       goto failed_out;
-
-               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 */
-               while (index < indirect_blks && count) {
-                       new_blocks[index++] = current_block++;
-                       count--;
-               }
-               if (count > 0) {
-                       /*
-                        * save the new block number
-                        * for the first direct block
-                        */
-                       new_blocks[index] = current_block;
-                       printk(KERN_INFO "%s returned more blocks than "
-                                               "requested\n", __func__);
-                       WARN_ON(1);
-                       break;
-               }
-       }
-
-       target = blks - count ;
-       blk_allocated = count;
-       if (!target)
-               goto allocated;
-       /* Now allocate data blocks */
-       memset(&ar, 0, sizeof(ar));
-       ar.inode = inode;
-       ar.goal = goal;
-       ar.len = target;
-       ar.logical = iblock;
-       if (S_ISREG(inode->i_mode))
-               /* enable in-core preallocation only for regular files */
-               ar.flags = EXT4_MB_HINT_DATA;
-
-       current_block = ext4_mb_new_blocks(handle, &ar, err);
-       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)) {
-               /*
-                * if the allocation failed and we didn't allocate
-                * any blocks before
-                */
-               goto failed_out;
-       }
-       if (!*err) {
-               if (target == blks) {
-                       /*
-                        * save the new block number
-                        * for the first direct block
-                        */
-                       new_blocks[index] = current_block;
-               }
-               blk_allocated += ar.len;
-       }
-allocated:
-       /* total number of blocks allocated for direct blocks */
-       ret = blk_allocated;
-       *err = 0;
-       return ret;
-failed_out:
-       for (i = 0; i < index; i++)
-               ext4_free_blocks(handle, inode, NULL, new_blocks[i], 1, 0);
-       return ret;
-}
-
-/**
- *     ext4_alloc_branch - allocate and set up a chain of blocks.
- *     @handle: handle for this transaction
- *     @inode: owner
- *     @indirect_blks: number of allocated indirect blocks
- *     @blks: number of allocated direct blocks
- *     @goal: preferred place for allocation
- *     @offsets: offsets (in the blocks) to store the pointers to next.
- *     @branch: place to store the chain in.
- *
- *     This function allocates blocks, zeroes out all but the last one,
- *     links them into chain and (if we are synchronous) writes them to disk.
- *     In other words, it prepares a branch that can be spliced onto the
- *     inode. It stores the information about that chain in the branch[], in
- *     the same format as ext4_get_branch() would do. We are calling it after
- *     we had read the existing part of chain and partial points to the last
- *     triple of that (one with zero ->key). Upon the exit we have the same
- *     picture as after the successful ext4_get_block(), except that in one
- *     place chain is disconnected - *branch->p is still zero (we did not
- *     set the last link), but branch->key contains the number that should
- *     be placed into *branch->p to fill that gap.
- *
- *     If allocation fails we free all blocks we've allocated (and forget
- *     their buffer_heads) and return the error value the from failed
- *     ext4_alloc_block() (normally -ENOSPC). Otherwise we set the chain
- *     as described above and return 0.
- */
-static int ext4_alloc_branch(handle_t *handle, struct inode *inode,
-                            ext4_lblk_t iblock, int indirect_blks,
-                            int *blks, ext4_fsblk_t goal,
-                            ext4_lblk_t *offsets, Indirect *branch)
-{
-       int blocksize = inode->i_sb->s_blocksize;
-       int i, n = 0;
-       int err = 0;
-       struct buffer_head *bh;
-       int num;
-       ext4_fsblk_t new_blocks[4];
-       ext4_fsblk_t current_block;
-
-       num = ext4_alloc_blocks(handle, inode, iblock, goal, indirect_blks,
-                               *blks, new_blocks, &err);
-       if (err)
-               return err;
-
-       branch[0].key = cpu_to_le32(new_blocks[0]);
-       /*
-        * metadata blocks and data blocks are allocated.
-        */
-       for (n = 1; n <= indirect_blks;  n++) {
-               /*
-                * Get buffer_head for parent block, zero it out
-                * and set the pointer to new one, then send
-                * parent to disk.
-                */
-               bh = sb_getblk(inode->i_sb, new_blocks[n-1]);
-               if (unlikely(!bh)) {
-                       err = -EIO;
-                       goto failed;
-               }
-
-               branch[n].bh = bh;
-               lock_buffer(bh);
-               BUFFER_TRACE(bh, "call get_create_access");
-               err = ext4_journal_get_create_access(handle, bh);
-               if (err) {
-                       /* Don't brelse(bh) here; it's done in
-                        * ext4_journal_forget() below */
-                       unlock_buffer(bh);
-                       goto failed;
-               }
-
-               memset(bh->b_data, 0, blocksize);
-               branch[n].p = (__le32 *) bh->b_data + offsets[n];
-               branch[n].key = cpu_to_le32(new_blocks[n]);
-               *branch[n].p = branch[n].key;
-               if (n == indirect_blks) {
-                       current_block = new_blocks[n];
-                       /*
-                        * End of chain, update the last new metablock of
-                        * the chain to point to the new allocated
-                        * data blocks numbers
-                        */
-                       for (i = 1; i < num; i++)
-                               *(branch[n].p + i) = cpu_to_le32(++current_block);
-               }
-               BUFFER_TRACE(bh, "marking uptodate");
-               set_buffer_uptodate(bh);
-               unlock_buffer(bh);
-
-               BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
-               err = ext4_handle_dirty_metadata(handle, inode, bh);
-               if (err)
-                       goto failed;
-       }
-       *blks = num;
-       return err;
-failed:
-       /* Allocation failed, free what we already allocated */
-       ext4_free_blocks(handle, inode, NULL, new_blocks[0], 1, 0);
-       for (i = 1; i <= n ; i++) {
-               /*
-                * branch[i].bh is newly allocated, so there is no
-                * need to revoke the block, which is why we don't
-                * need to set EXT4_FREE_BLOCKS_METADATA.
-                */
-               ext4_free_blocks(handle, inode, NULL, new_blocks[i], 1,
-                                EXT4_FREE_BLOCKS_FORGET);
-       }
-       for (i = n+1; i < indirect_blks; i++)
-               ext4_free_blocks(handle, inode, NULL, new_blocks[i], 1, 0);
-
-       ext4_free_blocks(handle, inode, NULL, new_blocks[i], num, 0);
-
-       return err;
-}
-
-/**
- * ext4_splice_branch - splice the allocated branch onto inode.
- * @handle: handle for this transaction
- * @inode: owner
- * @block: (logical) number of block we are adding
- * @chain: chain of indirect blocks (with a missing link - see
- *     ext4_alloc_branch)
- * @where: location of missing link
- * @num:   number of indirect blocks we are adding
- * @blks:  number of direct blocks we are adding
- *
- * This function fills the missing link and does all housekeeping needed in
- * inode (->i_blocks, etc.). In case of success we end up with the full
- * chain to new block and return 0.
- */
-static int ext4_splice_branch(handle_t *handle, struct inode *inode,
-                             ext4_lblk_t block, Indirect *where, int num,
-                             int blks)
-{
-       int i;
-       int err = 0;
-       ext4_fsblk_t current_block;
-
-       /*
-        * If we're splicing into a [td]indirect block (as opposed to the
-        * inode) then we need to get write access to the [td]indirect block
-        * before the splice.
-        */
-       if (where->bh) {
-               BUFFER_TRACE(where->bh, "get_write_access");
-               err = ext4_journal_get_write_access(handle, where->bh);
-               if (err)
-                       goto err_out;
-       }
-       /* That's it */
-
-       *where->p = where->key;
-
-       /*
-        * Update the host buffer_head or inode to point to more just allocated
-        * direct blocks blocks
-        */
-       if (num == 0 && blks > 1) {
-               current_block = le32_to_cpu(where->key) + 1;
-               for (i = 1; i < blks; i++)
-                       *(where->p + i) = cpu_to_le32(current_block++);
-       }
-
-       /* We are done with atomic stuff, now do the rest of housekeeping */
-       /* had we spliced it onto indirect block? */
-       if (where->bh) {
-               /*
-                * If we spliced it onto an indirect block, we haven't
-                * altered the inode.  Note however that if it is being spliced
-                * onto an indirect block at the very end of the file (the
-                * file is growing) then we *will* alter the inode to reflect
-                * the new i_size.  But that is not done here - it is done in
-                * generic_commit_write->__mark_inode_dirty->ext4_dirty_inode.
-                */
-               jbd_debug(5, "splicing indirect only\n");
-               BUFFER_TRACE(where->bh, "call ext4_handle_dirty_metadata");
-               err = ext4_handle_dirty_metadata(handle, inode, where->bh);
-               if (err)
-                       goto err_out;
-       } else {
-               /*
-                * OK, we spliced it into the inode itself on a direct block.
-                */
-               ext4_mark_inode_dirty(handle, inode);
-               jbd_debug(5, "splicing direct\n");
-       }
-       return err;
-
-err_out:
-       for (i = 1; i <= num; i++) {
-               /*
-                * branch[i].bh is newly allocated, so there is no
-                * need to revoke the block, which is why we don't
-                * need to set EXT4_FREE_BLOCKS_METADATA.
-                */
-               ext4_free_blocks(handle, inode, where[i].bh, 0, 1,
-                                EXT4_FREE_BLOCKS_FORGET);
-       }
-       ext4_free_blocks(handle, inode, NULL, le32_to_cpu(where[num].key),
-                        blks, 0);
-
-       return err;
-}
-
-/*
- * The ext4_ind_map_blocks() function handles non-extents inodes
- * (i.e., using the traditional indirect/double-indirect i_blocks
- * scheme) for ext4_map_blocks().
- *
- * Allocation strategy is simple: if we have to allocate something, we will
- * have to go the whole way to leaf. So let's do it before attaching anything
- * to tree, set linkage between the newborn blocks, write them if sync is
- * required, recheck the path, free and repeat if check fails, otherwise
- * set the last missing link (that will protect us from any truncate-generated
- * removals - all blocks on the path are immune now) and possibly force the
- * write on the parent block.
- * That has a nice additional property: no special recovery from the failed
- * allocations is needed - we simply release blocks and do not touch anything
- * reachable from inode.
- *
- * `handle' can be NULL if create == 0.
- *
- * return > 0, # of blocks mapped or allocated.
- * return = 0, if plain lookup failed.
- * return < 0, error case.
- *
- * The ext4_ind_get_blocks() function should be called with
- * down_write(&EXT4_I(inode)->i_data_sem) if allocating filesystem
- * blocks (i.e., flags has EXT4_GET_BLOCKS_CREATE set) or
- * down_read(&EXT4_I(inode)->i_data_sem) if not allocating file system
- * blocks.
- */
-static int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
-                              struct ext4_map_blocks *map,
-                              int flags)
-{
-       int err = -EIO;
-       ext4_lblk_t offsets[4];
-       Indirect chain[4];
-       Indirect *partial;
-       ext4_fsblk_t goal;
-       int indirect_blks;
-       int blocks_to_boundary = 0;
-       int depth;
-       int count = 0;
-       ext4_fsblk_t first_block = 0;
-
-       trace_ext4_ind_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
-       J_ASSERT(!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)));
-       J_ASSERT(handle != NULL || (flags & EXT4_GET_BLOCKS_CREATE) == 0);
-       depth = ext4_block_to_path(inode, map->m_lblk, offsets,
-                                  &blocks_to_boundary);
-
-       if (depth == 0)
-               goto out;
-
-       partial = ext4_get_branch(inode, depth, offsets, chain, &err);
-
-       /* Simplest case - block found, no allocation needed */
-       if (!partial) {
-               first_block = le32_to_cpu(chain[depth - 1].key);
-               count++;
-               /*map more blocks*/
-               while (count < map->m_len && count <= blocks_to_boundary) {
-                       ext4_fsblk_t blk;
-
-                       blk = le32_to_cpu(*(chain[depth-1].p + count));
-
-                       if (blk == first_block + count)
-                               count++;
-                       else
-                               break;
-               }
-               goto got_it;
-       }
-
-       /* Next simple case - plain lookup or failed read of indirect block */
-       if ((flags & EXT4_GET_BLOCKS_CREATE) == 0 || err == -EIO)
-               goto cleanup;
-
-       /*
-        * Okay, we need to do block allocation.
-       */
-       goal = ext4_find_goal(inode, map->m_lblk, partial);
-
-       /* the number of blocks need to allocate for [d,t]indirect blocks */
-       indirect_blks = (chain + depth) - partial - 1;
-
-       /*
-        * Next look up the indirect map to count the totoal number of
-        * direct blocks to allocate for this branch.
-        */
-       count = ext4_blks_to_allocate(partial, indirect_blks,
-                                     map->m_len, blocks_to_boundary);
-       /*
-        * Block out ext4_truncate while we alter the tree
-        */
-       err = ext4_alloc_branch(handle, inode, map->m_lblk, indirect_blks,
-                               &count, goal,
-                               offsets + (partial - chain), partial);
-
-       /*
-        * The ext4_splice_branch call will free and forget any buffers
-        * on the new chain if there is a failure, but that risks using
-        * up transaction credits, especially for bitmaps where the
-        * credits cannot be returned.  Can we handle this somehow?  We
-        * may need to return -EAGAIN upwards in the worst case.  --sct
-        */
-       if (!err)
-               err = ext4_splice_branch(handle, inode, map->m_lblk,
-                                        partial, indirect_blks, count);
-       if (err)
-               goto cleanup;
-
-       map->m_flags |= EXT4_MAP_NEW;
-
-       ext4_update_inode_fsync_trans(handle, inode, 1);
-got_it:
-       map->m_flags |= EXT4_MAP_MAPPED;
-       map->m_pblk = le32_to_cpu(chain[depth-1].key);
-       map->m_len = count;
-       if (count > blocks_to_boundary)
-               map->m_flags |= EXT4_MAP_BOUNDARY;
-       err = count;
-       /* Clean up and exit */
-       partial = chain + depth - 1;    /* the whole chain */
-cleanup:
-       while (partial > chain) {
-               BUFFER_TRACE(partial->bh, "call brelse");
-               brelse(partial->bh);
-               partial--;
-       }
-out:
-       trace_ext4_ind_map_blocks_exit(inode, map->m_lblk,
-                               map->m_pblk, map->m_len, err);
-       return err;
-}
-
 #ifdef CONFIG_QUOTA
 qsize_t *ext4_get_reserved_space(struct inode *inode)
 {
@@ -1071,33 +242,6 @@ qsize_t *ext4_get_reserved_space(struct inode *inode)
 }
 #endif
 
-/*
- * Calculate the number of metadata blocks need to reserve
- * to allocate a new block at @lblocks for non extent file based file
- */
-static int ext4_indirect_calc_metadata_amount(struct inode *inode,
-                                             sector_t lblock)
-{
-       struct ext4_inode_info *ei = EXT4_I(inode);
-       sector_t dind_mask = ~((sector_t)EXT4_ADDR_PER_BLOCK(inode->i_sb) - 1);
-       int blk_bits;
-
-       if (lblock < EXT4_NDIR_BLOCKS)
-               return 0;
-
-       lblock -= EXT4_NDIR_BLOCKS;
-
-       if (ei->i_da_metadata_calc_len &&
-           (lblock & dind_mask) == ei->i_da_metadata_calc_last_lblock) {
-               ei->i_da_metadata_calc_len++;
-               return 0;
-       }
-       ei->i_da_metadata_calc_last_lblock = lblock & dind_mask;
-       ei->i_da_metadata_calc_len = 1;
-       blk_bits = order_base_2(lblock);
-       return (blk_bits / EXT4_ADDR_PER_BLOCK_BITS(inode->i_sb)) + 1;
-}
-
 /*
  * Calculate the number of metadata blocks need to reserve
  * to allocate a block located at @lblock
@@ -1107,7 +251,7 @@ static int ext4_calc_metadata_amount(struct inode *inode, ext4_lblk_t lblock)
        if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
                return ext4_ext_calc_metadata_amount(inode, lblock);
 
-       return ext4_indirect_calc_metadata_amount(inode, lblock);
+       return ext4_ind_calc_metadata_amount(inode, lblock);
 }
 
 /*
@@ -1589,16 +733,6 @@ static int do_journal_get_write_access(handle_t *handle,
        return ret;
 }
 
-/*
- * Truncate blocks that were not used by write. We have to truncate the
- * pagecache as well so that corresponding buffers get properly unmapped.
- */
-static void ext4_truncate_failed_write(struct inode *inode)
-{
-       truncate_inode_pages(inode->i_mapping, inode->i_size);
-       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,
@@ -1863,6 +997,7 @@ static int ext4_journalled_write_end(struct file *file,
        if (new_i_size > inode->i_size)
                i_size_write(inode, pos+copied);
        ext4_set_inode_state(inode, EXT4_STATE_JDATA);
+       EXT4_I(inode)->i_datasync_tid = handle->h_transaction->t_tid;
        if (new_i_size > EXT4_I(inode)->i_disksize) {
                ext4_update_i_disksize(inode, new_i_size);
                ret2 = ext4_mark_inode_dirty(handle, inode);
@@ -2571,6 +1706,7 @@ static int __ext4_journalled_writepage(struct page *page,
                                write_end_fn);
        if (ret == 0)
                ret = err;
+       EXT4_I(inode)->i_datasync_tid = handle->h_transaction->t_tid;
        err = ext4_journal_stop(handle);
        if (!ret)
                ret = err;
@@ -3449,112 +2585,6 @@ static int ext4_releasepage(struct page *page, gfp_t wait)
                return try_to_free_buffers(page);
 }
 
-/*
- * O_DIRECT for ext3 (or indirect map) based files
- *
- * If the O_DIRECT write will extend the file then add this inode to the
- * orphan list.  So recovery will truncate it back to the original size
- * if the machine crashes during the write.
- *
- * If the O_DIRECT write is intantiating holes inside i_size and the machine
- * crashes then stale disk data _may_ be exposed inside the file. But current
- * VFS code falls back into buffered path in that case so we are safe.
- */
-static ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb,
-                             const struct iovec *iov, loff_t offset,
-                             unsigned long nr_segs)
-{
-       struct file *file = iocb->ki_filp;
-       struct inode *inode = file->f_mapping->host;
-       struct ext4_inode_info *ei = EXT4_I(inode);
-       handle_t *handle;
-       ssize_t ret;
-       int orphan = 0;
-       size_t count = iov_length(iov, nr_segs);
-       int retries = 0;
-
-       if (rw == WRITE) {
-               loff_t final_size = offset + count;
-
-               if (final_size > inode->i_size) {
-                       /* Credits for sb + inode write */
-                       handle = ext4_journal_start(inode, 2);
-                       if (IS_ERR(handle)) {
-                               ret = PTR_ERR(handle);
-                               goto out;
-                       }
-                       ret = ext4_orphan_add(handle, inode);
-                       if (ret) {
-                               ext4_journal_stop(handle);
-                               goto out;
-                       }
-                       orphan = 1;
-                       ei->i_disksize = inode->i_size;
-                       ext4_journal_stop(handle);
-               }
-       }
-
-retry:
-       if (rw == READ && ext4_should_dioread_nolock(inode))
-               ret = __blockdev_direct_IO(rw, iocb, inode,
-                                inode->i_sb->s_bdev, iov,
-                                offset, nr_segs,
-                                ext4_get_block, NULL, NULL, 0);
-       else {
-               ret = blockdev_direct_IO(rw, iocb, inode, iov,
-                                offset, nr_segs, ext4_get_block);
-
-               if (unlikely((rw & WRITE) && ret < 0)) {
-                       loff_t isize = i_size_read(inode);
-                       loff_t end = offset + iov_length(iov, nr_segs);
-
-                       if (end > isize)
-                               ext4_truncate_failed_write(inode);
-               }
-       }
-       if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
-               goto retry;
-
-       if (orphan) {
-               int err;
-
-               /* Credits for sb + inode write */
-               handle = ext4_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... */
-                       ret = PTR_ERR(handle);
-                       if (inode->i_nlink)
-                               ext4_orphan_del(NULL, inode);
-
-                       goto out;
-               }
-               if (inode->i_nlink)
-                       ext4_orphan_del(handle, inode);
-               if (ret > 0) {
-                       loff_t end = offset + ret;
-                       if (end > inode->i_size) {
-                               ei->i_disksize = end;
-                               i_size_write(inode, end);
-                               /*
-                                * We're going to return a positive `ret'
-                                * here due to non-zero-length I/O, so there's
-                                * no way of reporting error returns from
-                                * ext4_mark_inode_dirty() to userspace.  So
-                                * ignore it.
-                                */
-                               ext4_mark_inode_dirty(handle, inode);
-                       }
-               }
-               err = ext4_journal_stop(handle);
-               if (ret == 0)
-                       ret = err;
-       }
-out:
-       return ret;
-}
-
 /*
  * ext4_get_block used when preparing for a DIO write or buffer write.
  * We allocate an uinitialized extent if blocks haven't been allocated.
@@ -4033,383 +3063,6 @@ unlock:
        return err;
 }
 
-/*
- * Probably it should be a library function... search for first non-zero word
- * or memcmp with zero_page, whatever is better for particular architecture.
- * Linus?
- */
-static inline int all_zeroes(__le32 *p, __le32 *q)
-{
-       while (p < q)
-               if (*p++)
-                       return 0;
-       return 1;
-}
-
-/**
- *     ext4_find_shared - find the indirect blocks for partial truncation.
- *     @inode:   inode in question
- *     @depth:   depth of the affected branch
- *     @offsets: offsets of pointers in that branch (see ext4_block_to_path)
- *     @chain:   place to store the pointers to partial indirect blocks
- *     @top:     place to the (detached) top of branch
- *
- *     This is a helper function used by ext4_truncate().
- *
- *     When we do truncate() we may have to clean the ends of several
- *     indirect blocks but leave the blocks themselves alive. Block is
- *     partially truncated if some data below the new i_size is referred
- *     from it (and it is on the path to the first completely truncated
- *     data block, indeed).  We have to free the top of that path along
- *     with everything to the right of the path. Since no allocation
- *     past the truncation point is possible until ext4_truncate()
- *     finishes, we may safely do the latter, but top of branch may
- *     require special attention - pageout below the truncation point
- *     might try to populate it.
- *
- *     We atomically detach the top of branch from the tree, store the
- *     block number of its root in *@top, pointers to buffer_heads of
- *     partially truncated blocks - in @chain[].bh and pointers to
- *     their last elements that should not be removed - in
- *     @chain[].p. Return value is the pointer to last filled element
- *     of @chain.
- *
- *     The work left to caller to do the actual freeing of subtrees:
- *             a) free the subtree starting from *@top
- *             b) free the subtrees whose roots are stored in
- *                     (@chain[i].p+1 .. end of @chain[i].bh->b_data)
- *             c) free the subtrees growing from the inode past the @chain[0].
- *                     (no partially truncated stuff there).  */
-
-static Indirect *ext4_find_shared(struct inode *inode, int depth,
-                                 ext4_lblk_t offsets[4], Indirect chain[4],
-                                 __le32 *top)
-{
-       Indirect *partial, *p;
-       int k, err;
-
-       *top = 0;
-       /* Make k index the deepest non-null offset + 1 */
-       for (k = depth; k > 1 && !offsets[k-1]; k--)
-               ;
-       partial = ext4_get_branch(inode, k, offsets, chain, &err);
-       /* Writer: pointers */
-       if (!partial)
-               partial = chain + k-1;
-       /*
-        * If the branch acquired continuation since we've looked at it -
-        * fine, it should all survive and (new) top doesn't belong to us.
-        */
-       if (!partial->key && *partial->p)
-               /* Writer: end */
-               goto no_top;
-       for (p = partial; (p > chain) && all_zeroes((__le32 *) p->bh->b_data, p->p); p--)
-               ;
-       /*
-        * OK, we've found the last block that must survive. The rest of our
-        * branch should be detached before unlocking. However, if that rest
-        * of branch is all ours and does not grow immediately from the inode
-        * it's easier to cheat and just decrement partial->p.
-        */
-       if (p == chain + k - 1 && p > chain) {
-               p->p--;
-       } else {
-               *top = *p->p;
-               /* Nope, don't do this in ext4.  Must leave the tree intact */
-#if 0
-               *p->p = 0;
-#endif
-       }
-       /* Writer: end */
-
-       while (partial > p) {
-               brelse(partial->bh);
-               partial--;
-       }
-no_top:
-       return partial;
-}
-
-/*
- * Zero a number of block pointers in either an inode or an indirect block.
- * If we restart the transaction we must again get write access to the
- * indirect block for further modification.
- *
- * We release `count' blocks on disk, but (last - first) may be greater
- * than `count' because there can be holes in there.
- *
- * Return 0 on success, 1 on invalid block range
- * and < 0 on fatal error.
- */
-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 | EXT4_FREE_BLOCKS_VALIDATED;
-       int     err;
-
-       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(inode, "attempt to clear invalid "
-                                "blocks %llu len %lu",
-                                (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");
-                       err = ext4_handle_dirty_metadata(handle, inode, bh);
-                       if (unlikely(err))
-                               goto out_err;
-               }
-               err = ext4_mark_inode_dirty(handle, inode);
-               if (unlikely(err))
-                       goto out_err;
-               err = ext4_truncate_restart_trans(handle, inode,
-                                                 blocks_for_truncate(inode));
-               if (unlikely(err))
-                       goto out_err;
-               if (bh) {
-                       BUFFER_TRACE(bh, "retaking write access");
-                       err = ext4_journal_get_write_access(handle, bh);
-                       if (unlikely(err))
-                               goto out_err;
-               }
-       }
-
-       for (p = first; p < last; p++)
-               *p = 0;
-
-       ext4_free_blocks(handle, inode, NULL, block_to_free, count, flags);
-       return 0;
-out_err:
-       ext4_std_error(inode->i_sb, err);
-       return err;
-}
-
-/**
- * ext4_free_data - free a list of data blocks
- * @handle:    handle for this transaction
- * @inode:     inode we are dealing with
- * @this_bh:   indirect buffer_head which contains *@first and *@last
- * @first:     array of block numbers
- * @last:      points immediately past the end of array
- *
- * We are freeing all blocks referred from that array (numbers are stored as
- * little-endian 32-bit) and updating @inode->i_blocks appropriately.
- *
- * We accumulate contiguous runs of blocks to free.  Conveniently, if these
- * blocks are contiguous then releasing them at one time will only affect one
- * or two bitmap blocks (+ group descriptor(s) and superblock) and we won't
- * actually use a lot of journal space.
- *
- * @this_bh will be %NULL if @first and @last point into the inode's direct
- * block pointers.
- */
-static void ext4_free_data(handle_t *handle, struct inode *inode,
-                          struct buffer_head *this_bh,
-                          __le32 *first, __le32 *last)
-{
-       ext4_fsblk_t block_to_free = 0;    /* Starting block # of a run */
-       unsigned long count = 0;            /* Number of blocks in the run */
-       __le32 *block_to_free_p = NULL;     /* Pointer into inode/ind
-                                              corresponding to
-                                              block_to_free */
-       ext4_fsblk_t nr;                    /* Current block # */
-       __le32 *p;                          /* Pointer into inode/ind
-                                              for current block */
-       int err = 0;
-
-       if (this_bh) {                          /* For indirect block */
-               BUFFER_TRACE(this_bh, "get_write_access");
-               err = ext4_journal_get_write_access(handle, this_bh);
-               /* Important: if we can't update the indirect pointers
-                * to the blocks, we can't free them. */
-               if (err)
-                       return;
-       }
-
-       for (p = first; p < last; p++) {
-               nr = le32_to_cpu(*p);
-               if (nr) {
-                       /* accumulate blocks to free if they're contiguous */
-                       if (count == 0) {
-                               block_to_free = nr;
-                               block_to_free_p = p;
-                               count = 1;
-                       } else if (nr == block_to_free + count) {
-                               count++;
-                       } else {
-                               err = ext4_clear_blocks(handle, inode, this_bh,
-                                                       block_to_free, count,
-                                                       block_to_free_p, p);
-                               if (err)
-                                       break;
-                               block_to_free = nr;
-                               block_to_free_p = p;
-                               count = 1;
-                       }
-               }
-       }
-
-       if (!err && count > 0)
-               err = ext4_clear_blocks(handle, inode, this_bh, block_to_free,
-                                       count, block_to_free_p, p);
-       if (err < 0)
-               /* fatal error */
-               return;
-
-       if (this_bh) {
-               BUFFER_TRACE(this_bh, "call ext4_handle_dirty_metadata");
-
-               /*
-                * The buffer head should have an attached journal head at this
-                * point. However, if the data is corrupted and an indirect
-                * block pointed to itself, it would have been detached when
-                * the block was cleared. Check for this instead of OOPSing.
-                */
-               if ((EXT4_JOURNAL(inode) == NULL) || bh2jh(this_bh))
-                       ext4_handle_dirty_metadata(handle, inode, this_bh);
-               else
-                       EXT4_ERROR_INODE(inode,
-                                        "circular indirect block detected at "
-                                        "block %llu",
-                               (unsigned long long) this_bh->b_blocknr);
-       }
-}
-
-/**
- *     ext4_free_branches - free an array of branches
- *     @handle: JBD handle for this transaction
- *     @inode: inode we are dealing with
- *     @parent_bh: the buffer_head which contains *@first and *@last
- *     @first: array of block numbers
- *     @last:  pointer immediately past the end of array
- *     @depth: depth of the branches to free
- *
- *     We are freeing all blocks referred from these branches (numbers are
- *     stored as little-endian 32-bit) and updating @inode->i_blocks
- *     appropriately.
- */
-static void ext4_free_branches(handle_t *handle, struct inode *inode,
-                              struct buffer_head *parent_bh,
-                              __le32 *first, __le32 *last, int depth)
-{
-       ext4_fsblk_t nr;
-       __le32 *p;
-
-       if (ext4_handle_is_aborted(handle))
-               return;
-
-       if (depth--) {
-               struct buffer_head *bh;
-               int addr_per_block = EXT4_ADDR_PER_BLOCK(inode->i_sb);
-               p = last;
-               while (--p >= first) {
-                       nr = le32_to_cpu(*p);
-                       if (!nr)
-                               continue;               /* A hole */
-
-                       if (!ext4_data_block_valid(EXT4_SB(inode->i_sb),
-                                                  nr, 1)) {
-                               EXT4_ERROR_INODE(inode,
-                                                "invalid indirect mapped "
-                                                "block %lu (level %d)",
-                                                (unsigned long) nr, depth);
-                               break;
-                       }
-
-                       /* Go read the buffer for the next level down */
-                       bh = sb_bread(inode->i_sb, nr);
-
-                       /*
-                        * A read failure? Report error and clear slot
-                        * (should be rare).
-                        */
-                       if (!bh) {
-                               EXT4_ERROR_INODE_BLOCK(inode, nr,
-                                                      "Read failure");
-                               continue;
-                       }
-
-                       /* This zaps the entire block.  Bottom up. */
-                       BUFFER_TRACE(bh, "free child branches");
-                       ext4_free_branches(handle, inode, bh,
-                                       (__le32 *) bh->b_data,
-                                       (__le32 *) bh->b_data + addr_per_block,
-                                       depth);
-                       brelse(bh);
-
-                       /*
-                        * Everything below this this pointer has been
-                        * released.  Now let this top-of-subtree go.
-                        *
-                        * We want the freeing of this indirect block to be
-                        * atomic in the journal with the updating of the
-                        * bitmap block which owns it.  So make some room in
-                        * the journal.
-                        *
-                        * We zero the parent pointer *after* freeing its
-                        * pointee in the bitmaps, so if extend_transaction()
-                        * for some reason fails to put the bitmap changes and
-                        * the release into the same transaction, recovery
-                        * will merely complain about releasing a free block,
-                        * rather than leaking blocks.
-                        */
-                       if (ext4_handle_is_aborted(handle))
-                               return;
-                       if (try_to_extend_transaction(handle, inode)) {
-                               ext4_mark_inode_dirty(handle, inode);
-                               ext4_truncate_restart_trans(handle, inode,
-                                           blocks_for_truncate(inode));
-                       }
-
-                       /*
-                        * The forget flag here is critical because if
-                        * we are journaling (and not doing data
-                        * journaling), we have to make sure a revoke
-                        * record is written to prevent the journal
-                        * replay from overwriting the (former)
-                        * indirect block if it gets reallocated as a
-                        * data block.  This must happen in the same
-                        * transaction where the data blocks are
-                        * actually freed.
-                        */
-                       ext4_free_blocks(handle, inode, NULL, nr, 1,
-                                        EXT4_FREE_BLOCKS_METADATA|
-                                        EXT4_FREE_BLOCKS_FORGET);
-
-                       if (parent_bh) {
-                               /*
-                                * The block which we have just freed is
-                                * pointed to by an indirect block: journal it
-                                */
-                               BUFFER_TRACE(parent_bh, "get_write_access");
-                               if (!ext4_journal_get_write_access(handle,
-                                                                  parent_bh)){
-                                       *p = 0;
-                                       BUFFER_TRACE(parent_bh,
-                                       "call ext4_handle_dirty_metadata");
-                                       ext4_handle_dirty_metadata(handle,
-                                                                  inode,
-                                                                  parent_bh);
-                               }
-                       }
-               }
-       } else {
-               /* We have reached the bottom of the tree. */
-               BUFFER_TRACE(parent_bh, "free data blocks");
-               ext4_free_data(handle, inode, parent_bh, first, last);
-       }
-}
-
 int ext4_can_truncate(struct inode *inode)
 {
        if (S_ISREG(inode->i_mode))
@@ -4476,19 +3129,6 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
  */
 void ext4_truncate(struct inode *inode)
 {
-       handle_t *handle;
-       struct ext4_inode_info *ei = EXT4_I(inode);
-       __le32 *i_data = ei->i_data;
-       int addr_per_block = EXT4_ADDR_PER_BLOCK(inode->i_sb);
-       struct address_space *mapping = inode->i_mapping;
-       ext4_lblk_t offsets[4];
-       Indirect chain[4];
-       Indirect *partial;
-       __le32 nr = 0;
-       int n = 0;
-       ext4_lblk_t last_block, max_block;
-       unsigned blocksize = inode->i_sb->s_blocksize;
-
        trace_ext4_truncate_enter(inode);
 
        if (!ext4_can_truncate(inode))
@@ -4499,149 +3139,11 @@ void ext4_truncate(struct inode *inode)
        if (inode->i_size == 0 && !test_opt(inode->i_sb, NO_AUTO_DA_ALLOC))
                ext4_set_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE);
 
-       if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
+       if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
                ext4_ext_truncate(inode);
-               trace_ext4_truncate_exit(inode);
-               return;
-       }
-
-       handle = start_transaction(inode);
-       if (IS_ERR(handle))
-               return;         /* AKPM: return what? */
-
-       last_block = (inode->i_size + blocksize-1)
-                                       >> EXT4_BLOCK_SIZE_BITS(inode->i_sb);
-       max_block = (EXT4_SB(inode->i_sb)->s_bitmap_maxbytes + blocksize-1)
-                                       >> EXT4_BLOCK_SIZE_BITS(inode->i_sb);
-
-       if (inode->i_size & (blocksize - 1))
-               if (ext4_block_truncate_page(handle, mapping, inode->i_size))
-                       goto out_stop;
-
-       if (last_block != max_block) {
-               n = ext4_block_to_path(inode, last_block, offsets, NULL);
-               if (n == 0)
-                       goto out_stop;  /* error */
-       }
-
-       /*
-        * OK.  This truncate is going to happen.  We add the inode to the
-        * orphan list, so that if this truncate spans multiple transactions,
-        * and we crash, we will resume the truncate when the filesystem
-        * recovers.  It also marks the inode dirty, to catch the new size.
-        *
-        * Implication: the file must always be in a sane, consistent
-        * truncatable state while each transaction commits.
-        */
-       if (ext4_orphan_add(handle, inode))
-               goto out_stop;
-
-       /*
-        * From here we block out all ext4_get_block() callers who want to
-        * modify the block allocation tree.
-        */
-       down_write(&ei->i_data_sem);
-
-       ext4_discard_preallocations(inode);
-
-       /*
-        * The orphan list entry will now protect us from any crash which
-        * occurs before the truncate completes, so it is now safe to propagate
-        * the new, shorter inode size (held for now in i_size) into the
-        * on-disk inode. We do this via i_disksize, which is the value which
-        * ext4 *really* writes onto the disk inode.
-        */
-       ei->i_disksize = inode->i_size;
-
-       if (last_block == max_block) {
-               /*
-                * It is unnecessary to free any data blocks if last_block is
-                * equal to the indirect block limit.
-                */
-               goto out_unlock;
-       } else if (n == 1) {            /* direct blocks */
-               ext4_free_data(handle, inode, NULL, i_data+offsets[0],
-                              i_data + EXT4_NDIR_BLOCKS);
-               goto do_indirects;
-       }
-
-       partial = ext4_find_shared(inode, n, offsets, chain, &nr);
-       /* Kill the top of shared branch (not detached) */
-       if (nr) {
-               if (partial == chain) {
-                       /* Shared branch grows from the inode */
-                       ext4_free_branches(handle, inode, NULL,
-                                          &nr, &nr+1, (chain+n-1) - partial);
-                       *partial->p = 0;
-                       /*
-                        * We mark the inode dirty prior to restart,
-                        * and prior to stop.  No need for it here.
-                        */
-               } else {
-                       /* Shared branch grows from an indirect block */
-                       BUFFER_TRACE(partial->bh, "get_write_access");
-                       ext4_free_branches(handle, inode, partial->bh,
-                                       partial->p,
-                                       partial->p+1, (chain+n-1) - partial);
-               }
-       }
-       /* Clear the ends of indirect blocks on the shared branch */
-       while (partial > chain) {
-               ext4_free_branches(handle, inode, partial->bh, partial->p + 1,
-                                  (__le32*)partial->bh->b_data+addr_per_block,
-                                  (chain+n-1) - partial);
-               BUFFER_TRACE(partial->bh, "call brelse");
-               brelse(partial->bh);
-               partial--;
-       }
-do_indirects:
-       /* Kill the remaining (whole) subtrees */
-       switch (offsets[0]) {
-       default:
-               nr = i_data[EXT4_IND_BLOCK];
-               if (nr) {
-                       ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 1);
-                       i_data[EXT4_IND_BLOCK] = 0;
-               }
-       case EXT4_IND_BLOCK:
-               nr = i_data[EXT4_DIND_BLOCK];
-               if (nr) {
-                       ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 2);
-                       i_data[EXT4_DIND_BLOCK] = 0;
-               }
-       case EXT4_DIND_BLOCK:
-               nr = i_data[EXT4_TIND_BLOCK];
-               if (nr) {
-                       ext4_free_branches(handle, inode, NULL, &nr, &nr+1, 3);
-                       i_data[EXT4_TIND_BLOCK] = 0;
-               }
-       case EXT4_TIND_BLOCK:
-               ;
-       }
-
-out_unlock:
-       up_write(&ei->i_data_sem);
-       inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
-       ext4_mark_inode_dirty(handle, inode);
-
-       /*
-        * In a multi-transaction truncate, we only make the final transaction
-        * synchronous
-        */
-       if (IS_SYNC(inode))
-               ext4_handle_sync(handle);
-out_stop:
-       /*
-        * If this was a simple ftruncate(), and the file will remain alive
-        * then we need to clear up the orphan record which we created above.
-        * However, if this was a real unlink then we were called by
-        * ext4_delete_inode(), and we allow that function to clean up the
-        * orphan info for us.
-        */
-       if (inode->i_nlink)
-               ext4_orphan_del(handle, inode);
+       else
+               ext4_ind_truncate(inode);
 
-       ext4_journal_stop(handle);
        trace_ext4_truncate_exit(inode);
 }
 
@@ -5012,7 +3514,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
                   (S_ISLNK(inode->i_mode) &&
                    !ext4_inode_is_fast_symlink(inode))) {
                /* Validate block references which are part of inode */
-               ret = ext4_check_inode_blockref(inode);
+               ret = ext4_ind_check_inode(inode);
        }
        if (ret)
                goto bad_inode;
@@ -5459,34 +3961,10 @@ int ext4_getattr(struct vfsmount *mnt, struct dentry *dentry,
        return 0;
 }
 
-static int ext4_indirect_trans_blocks(struct inode *inode, int nrblocks,
-                                     int chunk)
-{
-       int indirects;
-
-       /* if nrblocks are contiguous */
-       if (chunk) {
-               /*
-                * With N contiguous data blocks, we need at most
-                * N/EXT4_ADDR_PER_BLOCK(inode->i_sb) + 1 indirect blocks,
-                * 2 dindirect blocks, and 1 tindirect block
-                */
-               return DIV_ROUND_UP(nrblocks,
-                                   EXT4_ADDR_PER_BLOCK(inode->i_sb)) + 4;
-       }
-       /*
-        * if nrblocks are not contiguous, worse case, each block touch
-        * a indirect block, and each indirect block touch a double indirect
-        * block, plus a triple indirect block
-        */
-       indirects = nrblocks * 2 + 1;
-       return indirects;
-}
-
 static int ext4_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
 {
        if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
-               return ext4_indirect_trans_blocks(inode, nrblocks, chunk);
+               return ext4_ind_trans_blocks(inode, nrblocks, chunk);
        return ext4_ext_index_trans_blocks(inode, nrblocks, chunk);
 }
 
index 808c554e773fdc2658c4708f1697edabab665acc..f18bfe37aff845cedd3dd3acf6c9d0e2225926ae 100644 (file)
@@ -202,8 +202,9 @@ setversion_out:
                struct super_block *sb = inode->i_sb;
                int err, err2=0;
 
-               if (!capable(CAP_SYS_RESOURCE))
-                       return -EPERM;
+               err = ext4_resize_begin(sb);
+               if (err)
+                       return err;
 
                if (get_user(n_blocks_count, (__u32 __user *)arg))
                        return -EFAULT;
@@ -221,6 +222,7 @@ setversion_out:
                if (err == 0)
                        err = err2;
                mnt_drop_write(filp->f_path.mnt);
+               ext4_resize_end(sb);
 
                return err;
        }
@@ -271,8 +273,9 @@ mext_out:
                struct super_block *sb = inode->i_sb;
                int err, err2=0;
 
-               if (!capable(CAP_SYS_RESOURCE))
-                       return -EPERM;
+               err = ext4_resize_begin(sb);
+               if (err)
+                       return err;
 
                if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg,
                                sizeof(input)))
@@ -291,6 +294,7 @@ mext_out:
                if (err == 0)
                        err = err2;
                mnt_drop_write(filp->f_path.mnt);
+               ext4_resize_end(sb);
 
                return err;
        }
index 6ed859d56850494d440dbbacc56967c4538fc659..17a5a57c415a2dcdd36104c08473438c238f38ee 100644 (file)
@@ -75,8 +75,8 @@
  *
  * The inode preallocation space is used looking at the _logical_ start
  * block. If only the logical file block falls within the range of prealloc
- * space we will consume the particular prealloc space. This make sure that
- * that the we have contiguous physical blocks representing the file blocks
+ * space we will consume the particular prealloc space. This makes sure that
+ * we have contiguous physical blocks representing the file blocks
  *
  * The important thing to be noted in case of inode prealloc space is that
  * we don't modify the values associated to inode prealloc space except
@@ -84,7 +84,7 @@
  *
  * If we are not able to find blocks in the inode prealloc space and if we
  * have the group allocation flag set then we look at the locality group
- * prealloc space. These are per CPU prealloc list repreasented as
+ * prealloc space. These are per CPU prealloc list represented as
  *
  * ext4_sb_info.s_locality_groups[smp_processor_id()]
  *
  * we are doing a group prealloc we try to normalize the request to
  * sbi->s_mb_group_prealloc. Default value of s_mb_group_prealloc is
  * 512 blocks. This can be tuned via
- * /sys/fs/ext4/<partition/mb_group_prealloc. The value is represented in
+ * /sys/fs/ext4/<partition>/mb_group_prealloc. The value is represented in
  * terms of number of blocks. If we have mounted the file system with -O
  * stripe=<value> option the group prealloc request is normalized to the
- * stripe value (sbi->s_stripe)
+ * the smallest multiple of the stripe value (sbi->s_stripe) which is
+ * greater than the default mb_group_prealloc.
  *
- * The regular allocator(using the buddy cache) supports few tunables.
+ * The regular allocator (using the buddy cache) supports a few tunables.
  *
  * /sys/fs/ext4/<partition>/mb_min_to_scan
  * /sys/fs/ext4/<partition>/mb_max_to_scan
  * best extent in the found extents. Searching for the blocks starts with
  * the group specified as the goal value in allocation context via
  * ac_g_ex. Each group is first checked based on the criteria whether it
- * can used for allocation. ext4_mb_good_group explains how the groups are
+ * can be used for allocation. ext4_mb_good_group explains how the groups are
  * checked.
  *
  * Both the prealloc space are getting populated as above. So for the first
@@ -492,10 +493,11 @@ static void mb_cmp_bitmaps(struct ext4_buddy *e4b, void *bitmap)
                b2 = (unsigned char *) bitmap;
                for (i = 0; i < e4b->bd_sb->s_blocksize; i++) {
                        if (b1[i] != b2[i]) {
-                               printk(KERN_ERR "corruption in group %u "
-                                      "at byte %u(%u): %x in copy != %x "
-                                      "on disk/prealloc\n",
-                                      e4b->bd_group, i, i * 8, b1[i], b2[i]);
+                               ext4_msg(e4b->bd_sb, KERN_ERR,
+                                        "corruption in group %u "
+                                        "at byte %u(%u): %x in copy != %x "
+                                        "on disk/prealloc",
+                                        e4b->bd_group, i, i * 8, b1[i], b2[i]);
                                BUG();
                        }
                }
@@ -1125,7 +1127,7 @@ ext4_mb_load_buddy(struct super_block *sb, ext4_group_t group,
        grp = ext4_get_group_info(sb, group);
 
        e4b->bd_blkbits = sb->s_blocksize_bits;
-       e4b->bd_info = ext4_get_group_info(sb, group);
+       e4b->bd_info = grp;
        e4b->bd_sb = sb;
        e4b->bd_group = group;
        e4b->bd_buddy_page = NULL;
@@ -1281,7 +1283,7 @@ static void mb_clear_bits(void *bm, int cur, int len)
        }
 }
 
-static void mb_set_bits(void *bm, int cur, int len)
+void ext4_set_bits(void *bm, int cur, int len)
 {
        __u32 *addr;
 
@@ -1510,7 +1512,7 @@ static int mb_mark_used(struct ext4_buddy *e4b, struct ext4_free_extent *ex)
        }
        mb_set_largest_free_order(e4b->bd_sb, e4b->bd_info);
 
-       mb_set_bits(EXT4_MB_BITMAP(e4b), ex->fe_start, len0);
+       ext4_set_bits(EXT4_MB_BITMAP(e4b), ex->fe_start, len0);
        mb_check_buddy(e4b);
 
        return ret;
@@ -2223,8 +2225,8 @@ int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group,
                        EXT4_DESC_PER_BLOCK_BITS(sb);
                meta_group_info = kmalloc(metalen, GFP_KERNEL);
                if (meta_group_info == NULL) {
-                       printk(KERN_ERR "EXT4-fs: can't allocate mem for a "
-                              "buddy group\n");
+                       ext4_msg(sb, KERN_ERR, "EXT4-fs: can't allocate mem "
+                                "for a buddy group");
                        goto exit_meta_group_info;
                }
                sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)] =
@@ -2237,7 +2239,7 @@ int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group,
 
        meta_group_info[i] = kmem_cache_alloc(cachep, GFP_KERNEL);
        if (meta_group_info[i] == NULL) {
-               printk(KERN_ERR "EXT4-fs: can't allocate buddy mem\n");
+               ext4_msg(sb, KERN_ERR, "EXT4-fs: can't allocate buddy mem");
                goto exit_group_info;
        }
        memset(meta_group_info[i], 0, kmem_cache_size(cachep));
@@ -2279,8 +2281,10 @@ int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group,
 
 exit_group_info:
        /* If a meta_group_info table has been allocated, release it now */
-       if (group % EXT4_DESC_PER_BLOCK(sb) == 0)
+       if (group % EXT4_DESC_PER_BLOCK(sb) == 0) {
                kfree(sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)]);
+               sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)] = NULL;
+       }
 exit_meta_group_info:
        return -ENOMEM;
 } /* ext4_mb_add_groupinfo */
@@ -2328,23 +2332,26 @@ static int ext4_mb_init_backend(struct super_block *sb)
        /* An 8TB filesystem with 64-bit pointers requires a 4096 byte
         * kmalloc. A 128kb malloc should suffice for a 256TB filesystem.
         * So a two level scheme suffices for now. */
-       sbi->s_group_info = kzalloc(array_size, GFP_KERNEL);
+       sbi->s_group_info = ext4_kvzalloc(array_size, GFP_KERNEL);
        if (sbi->s_group_info == NULL) {
-               printk(KERN_ERR "EXT4-fs: can't allocate buddy meta group\n");
+               ext4_msg(sb, KERN_ERR, "can't allocate buddy meta group");
                return -ENOMEM;
        }
        sbi->s_buddy_cache = new_inode(sb);
        if (sbi->s_buddy_cache == NULL) {
-               printk(KERN_ERR "EXT4-fs: can't get new inode\n");
+               ext4_msg(sb, KERN_ERR, "can't get new inode");
                goto err_freesgi;
        }
-       sbi->s_buddy_cache->i_ino = get_next_ino();
+       /* To avoid potentially colliding with an valid on-disk inode number,
+        * use EXT4_BAD_INO for the buddy cache inode number.  This inode is
+        * not in the inode hash, so it should never be found by iget(), but
+        * this will avoid confusion if it ever shows up during debugging. */
+       sbi->s_buddy_cache->i_ino = EXT4_BAD_INO;
        EXT4_I(sbi->s_buddy_cache)->i_disksize = 0;
        for (i = 0; i < ngroups; i++) {
                desc = ext4_get_group_desc(sb, i, NULL);
                if (desc == NULL) {
-                       printk(KERN_ERR
-                               "EXT4-fs: can't read descriptor %u\n", i);
+                       ext4_msg(sb, KERN_ERR, "can't read descriptor %u", i);
                        goto err_freebuddy;
                }
                if (ext4_mb_add_groupinfo(sb, i, desc) != 0)
@@ -2362,7 +2369,7 @@ err_freebuddy:
                kfree(sbi->s_group_info[i]);
        iput(sbi->s_buddy_cache);
 err_freesgi:
-       kfree(sbi->s_group_info);
+       ext4_kvfree(sbi->s_group_info);
        return -ENOMEM;
 }
 
@@ -2404,14 +2411,15 @@ static int ext4_groupinfo_create_slab(size_t size)
                                        slab_size, 0, SLAB_RECLAIM_ACCOUNT,
                                        NULL);
 
+       ext4_groupinfo_caches[cache_index] = cachep;
+
        mutex_unlock(&ext4_grpinfo_slab_create_mutex);
        if (!cachep) {
-               printk(KERN_EMERG "EXT4: no memory for groupinfo slab cache\n");
+               printk(KERN_EMERG
+                      "EXT4-fs: no memory for groupinfo slab cache\n");
                return -ENOMEM;
        }
 
-       ext4_groupinfo_caches[cache_index] = cachep;
-
        return 0;
 }
 
@@ -2457,12 +2465,6 @@ int ext4_mb_init(struct super_block *sb, int needs_recovery)
                i++;
        } while (i <= sb->s_blocksize_bits + 1);
 
-       /* init file for buddy data */
-       ret = ext4_mb_init_backend(sb);
-       if (ret != 0) {
-               goto out;
-       }
-
        spin_lock_init(&sbi->s_md_lock);
        spin_lock_init(&sbi->s_bal_lock);
 
@@ -2472,6 +2474,18 @@ int ext4_mb_init(struct super_block *sb, int needs_recovery)
        sbi->s_mb_stream_request = MB_DEFAULT_STREAM_THRESHOLD;
        sbi->s_mb_order2_reqs = MB_DEFAULT_ORDER2_REQS;
        sbi->s_mb_group_prealloc = MB_DEFAULT_GROUP_PREALLOC;
+       /*
+        * If there is a s_stripe > 1, then we set the s_mb_group_prealloc
+        * to the lowest multiple of s_stripe which is bigger than
+        * the s_mb_group_prealloc as determined above. We want
+        * the preallocation size to be an exact multiple of the
+        * RAID stripe size so that preallocations don't fragment
+        * the stripes.
+        */
+       if (sbi->s_stripe > 1) {
+               sbi->s_mb_group_prealloc = roundup(
+                       sbi->s_mb_group_prealloc, sbi->s_stripe);
+       }
 
        sbi->s_locality_groups = alloc_percpu(struct ext4_locality_group);
        if (sbi->s_locality_groups == NULL) {
@@ -2487,6 +2501,12 @@ int ext4_mb_init(struct super_block *sb, int needs_recovery)
                spin_lock_init(&lg->lg_prealloc_lock);
        }
 
+       /* init file for buddy data */
+       ret = ext4_mb_init_backend(sb);
+       if (ret != 0) {
+               goto out;
+       }
+
        if (sbi->s_proc)
                proc_create_data("mb_groups", S_IRUGO, sbi->s_proc,
                                 &ext4_mb_seq_groups_fops, sb);
@@ -2544,32 +2564,32 @@ int ext4_mb_release(struct super_block *sb)
                        EXT4_DESC_PER_BLOCK_BITS(sb);
                for (i = 0; i < num_meta_group_infos; i++)
                        kfree(sbi->s_group_info[i]);
-               kfree(sbi->s_group_info);
+               ext4_kvfree(sbi->s_group_info);
        }
        kfree(sbi->s_mb_offsets);
        kfree(sbi->s_mb_maxs);
        if (sbi->s_buddy_cache)
                iput(sbi->s_buddy_cache);
        if (sbi->s_mb_stats) {
-               printk(KERN_INFO
-                      "EXT4-fs: mballoc: %u blocks %u reqs (%u success)\n",
+               ext4_msg(sb, KERN_INFO,
+                      "mballoc: %u blocks %u reqs (%u success)",
                                atomic_read(&sbi->s_bal_allocated),
                                atomic_read(&sbi->s_bal_reqs),
                                atomic_read(&sbi->s_bal_success));
-               printk(KERN_INFO
-                     "EXT4-fs: mballoc: %u extents scanned, %u goal hits, "
-                               "%u 2^N hits, %u breaks, %u lost\n",
+               ext4_msg(sb, KERN_INFO,
+                     "mballoc: %u extents scanned, %u goal hits, "
+                               "%u 2^N hits, %u breaks, %u lost",
                                atomic_read(&sbi->s_bal_ex_scanned),
                                atomic_read(&sbi->s_bal_goals),
                                atomic_read(&sbi->s_bal_2orders),
                                atomic_read(&sbi->s_bal_breaks),
                                atomic_read(&sbi->s_mb_lost_chunks));
-               printk(KERN_INFO
-                      "EXT4-fs: mballoc: %lu generated and it took %Lu\n",
-                               sbi->s_mb_buddies_generated++,
+               ext4_msg(sb, KERN_INFO,
+                      "mballoc: %lu generated and it took %Lu",
+                               sbi->s_mb_buddies_generated,
                                sbi->s_mb_generation_time);
-               printk(KERN_INFO
-                      "EXT4-fs: mballoc: %u preallocated, %u discarded\n",
+               ext4_msg(sb, KERN_INFO,
+                      "mballoc: %u preallocated, %u discarded",
                                atomic_read(&sbi->s_mb_preallocated),
                                atomic_read(&sbi->s_mb_discarded));
        }
@@ -2628,6 +2648,15 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn)
                rb_erase(&entry->node, &(db->bb_free_root));
                mb_free_blocks(NULL, &e4b, entry->start_blk, entry->count);
 
+               /*
+                * Clear the trimmed flag for the group so that the next
+                * ext4_trim_fs can trim it.
+                * If the volume is mounted with -o discard, online discard
+                * is supported and the free blocks will be trimmed online.
+                */
+               if (!test_opt(sb, DISCARD))
+                       EXT4_MB_GRP_CLEAR_TRIMMED(db);
+
                if (!db->bb_free_root.rb_node) {
                        /* No more items in the per group rb tree
                         * balance refcounts from ext4_mb_free_metadata()
@@ -2771,8 +2800,8 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
                 * We leak some of the blocks here.
                 */
                ext4_lock_group(sb, ac->ac_b_ex.fe_group);
-               mb_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start,
-                           ac->ac_b_ex.fe_len);
+               ext4_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start,
+                             ac->ac_b_ex.fe_len);
                ext4_unlock_group(sb, ac->ac_b_ex.fe_group);
                err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh);
                if (!err)
@@ -2790,7 +2819,8 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac,
                }
        }
 #endif
-       mb_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start,ac->ac_b_ex.fe_len);
+       ext4_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start,
+                     ac->ac_b_ex.fe_len);
        if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
                gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
                ext4_free_blks_set(sb, gdp,
@@ -2830,8 +2860,9 @@ out_err:
 
 /*
  * here we normalize request for locality group
- * Group request are normalized to s_strip size if we set the same via mount
- * option. If not we set it to s_mb_group_prealloc which can be configured via
+ * Group request are normalized to s_mb_group_prealloc, which goes to
+ * s_strip if we set the same via mount option.
+ * s_mb_group_prealloc can be configured via
  * /sys/fs/ext4/<partition>/mb_group_prealloc
  *
  * XXX: should we try to preallocate more than the group has now?
@@ -2842,10 +2873,7 @@ static void ext4_mb_normalize_group_request(struct ext4_allocation_context *ac)
        struct ext4_locality_group *lg = ac->ac_lg;
 
        BUG_ON(lg == NULL);
-       if (EXT4_SB(sb)->s_stripe)
-               ac->ac_g_ex.fe_len = EXT4_SB(sb)->s_stripe;
-       else
-               ac->ac_g_ex.fe_len = EXT4_SB(sb)->s_mb_group_prealloc;
+       ac->ac_g_ex.fe_len = EXT4_SB(sb)->s_mb_group_prealloc;
        mb_debug(1, "#%u: goal %u blocks for locality group\n",
                current->pid, ac->ac_g_ex.fe_len);
 }
@@ -3001,9 +3029,10 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac,
 
        if (start + size <= ac->ac_o_ex.fe_logical &&
                        start > ac->ac_o_ex.fe_logical) {
-               printk(KERN_ERR "start %lu, size %lu, fe_logical %lu\n",
-                       (unsigned long) start, (unsigned long) size,
-                       (unsigned long) ac->ac_o_ex.fe_logical);
+               ext4_msg(ac->ac_sb, KERN_ERR,
+                        "start %lu, size %lu, fe_logical %lu",
+                        (unsigned long) start, (unsigned long) size,
+                        (unsigned long) ac->ac_o_ex.fe_logical);
        }
        BUG_ON(start + size <= ac->ac_o_ex.fe_logical &&
                        start > ac->ac_o_ex.fe_logical);
@@ -3262,7 +3291,7 @@ static void ext4_mb_generate_from_freelist(struct super_block *sb, void *bitmap,
 
        while (n) {
                entry = rb_entry(n, struct ext4_free_data, node);
-               mb_set_bits(bitmap, entry->start_blk, entry->count);
+               ext4_set_bits(bitmap, entry->start_blk, entry->count);
                n = rb_next(n);
        }
        return;
@@ -3304,7 +3333,7 @@ void ext4_mb_generate_from_pa(struct super_block *sb, void *bitmap,
                if (unlikely(len == 0))
                        continue;
                BUG_ON(groupnr != group);
-               mb_set_bits(bitmap, start, len);
+               ext4_set_bits(bitmap, start, len);
                preallocated += len;
                count++;
        }
@@ -3584,10 +3613,11 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh,
                bit = next + 1;
        }
        if (free != pa->pa_free) {
-               printk(KERN_CRIT "pa %p: logic %lu, phys. %lu, len %lu\n",
-                       pa, (unsigned long) pa->pa_lstart,
-                       (unsigned long) pa->pa_pstart,
-                       (unsigned long) pa->pa_len);
+               ext4_msg(e4b->bd_sb, KERN_CRIT,
+                        "pa %p: logic %lu, phys. %lu, len %lu",
+                        pa, (unsigned long) pa->pa_lstart,
+                        (unsigned long) pa->pa_pstart,
+                        (unsigned long) pa->pa_len);
                ext4_grp_locked_error(sb, group, 0, 0, "free %u, pa_free %u",
                                        free, pa->pa_free);
                /*
@@ -3775,7 +3805,8 @@ repeat:
                         * use preallocation while we're discarding it */
                        spin_unlock(&pa->pa_lock);
                        spin_unlock(&ei->i_prealloc_lock);
-                       printk(KERN_ERR "uh-oh! used pa while discarding\n");
+                       ext4_msg(sb, KERN_ERR,
+                                "uh-oh! used pa while discarding");
                        WARN_ON(1);
                        schedule_timeout_uninterruptible(HZ);
                        goto repeat;
@@ -3852,12 +3883,13 @@ static void ext4_mb_show_ac(struct ext4_allocation_context *ac)
            (EXT4_SB(sb)->s_mount_flags & EXT4_MF_FS_ABORTED))
                return;
 
-       printk(KERN_ERR "EXT4-fs: Can't allocate:"
-                       " Allocation context details:\n");
-       printk(KERN_ERR "EXT4-fs: status %d flags %d\n",
+       ext4_msg(ac->ac_sb, KERN_ERR, "EXT4-fs: Can't allocate:"
+                       " Allocation context details:");
+       ext4_msg(ac->ac_sb, KERN_ERR, "EXT4-fs: status %d flags %d",
                        ac->ac_status, ac->ac_flags);
-       printk(KERN_ERR "EXT4-fs: orig %lu/%lu/%lu@%lu, goal %lu/%lu/%lu@%lu, "
-                       "best %lu/%lu/%lu@%lu cr %d\n",
+       ext4_msg(ac->ac_sb, KERN_ERR, "EXT4-fs: orig %lu/%lu/%lu@%lu, "
+                       "goal %lu/%lu/%lu@%lu, "
+                       "best %lu/%lu/%lu@%lu cr %d",
                        (unsigned long)ac->ac_o_ex.fe_group,
                        (unsigned long)ac->ac_o_ex.fe_start,
                        (unsigned long)ac->ac_o_ex.fe_len,
@@ -3871,9 +3903,9 @@ static void ext4_mb_show_ac(struct ext4_allocation_context *ac)
                        (unsigned long)ac->ac_b_ex.fe_len,
                        (unsigned long)ac->ac_b_ex.fe_logical,
                        (int)ac->ac_criteria);
-       printk(KERN_ERR "EXT4-fs: %lu scanned, %d found\n", ac->ac_ex_scanned,
-               ac->ac_found);
-       printk(KERN_ERR "EXT4-fs: groups: \n");
+       ext4_msg(ac->ac_sb, KERN_ERR, "EXT4-fs: %lu scanned, %d found",
+                ac->ac_ex_scanned, ac->ac_found);
+       ext4_msg(ac->ac_sb, KERN_ERR, "EXT4-fs: groups: ");
        ngroups = ext4_get_groups_count(sb);
        for (i = 0; i < ngroups; i++) {
                struct ext4_group_info *grp = ext4_get_group_info(sb, i);
@@ -4637,7 +4669,7 @@ do_more:
        }
        ext4_mark_super_dirty(sb);
 error_return:
-       if (freed)
+       if (freed && !(flags & EXT4_FREE_BLOCKS_NO_QUOT_UPDATE))
                dquot_free_block(inode, freed);
        brelse(bitmap_bh);
        ext4_std_error(sb, err);
@@ -4645,7 +4677,7 @@ error_return:
 }
 
 /**
- * ext4_add_groupblocks() -- Add given blocks to an existing group
+ * ext4_group_add_blocks() -- Add given blocks to an existing group
  * @handle:                    handle to this transaction
  * @sb:                                super block
  * @block:                     start physcial block to add to the block group
@@ -4653,7 +4685,7 @@ error_return:
  *
  * This marks the blocks as free in the bitmap and buddy.
  */
-void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
+int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
                         ext4_fsblk_t block, unsigned long count)
 {
        struct buffer_head *bitmap_bh = NULL;
@@ -4666,25 +4698,35 @@ void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
        struct ext4_buddy e4b;
        int err = 0, ret, blk_free_count;
        ext4_grpblk_t blocks_freed;
-       struct ext4_group_info *grp;
 
        ext4_debug("Adding block(s) %llu-%llu\n", block, block + count - 1);
 
+       if (count == 0)
+               return 0;
+
        ext4_get_group_no_and_offset(sb, block, &block_group, &bit);
-       grp = ext4_get_group_info(sb, block_group);
        /*
         * Check to see if we are freeing blocks across a group
         * boundary.
         */
-       if (bit + count > EXT4_BLOCKS_PER_GROUP(sb))
+       if (bit + count > EXT4_BLOCKS_PER_GROUP(sb)) {
+               ext4_warning(sb, "too much blocks added to group %u\n",
+                            block_group);
+               err = -EINVAL;
                goto error_return;
+       }
 
        bitmap_bh = ext4_read_block_bitmap(sb, block_group);
-       if (!bitmap_bh)
+       if (!bitmap_bh) {
+               err = -EIO;
                goto error_return;
+       }
+
        desc = ext4_get_group_desc(sb, block_group, &gd_bh);
-       if (!desc)
+       if (!desc) {
+               err = -EIO;
                goto error_return;
+       }
 
        if (in_range(ext4_block_bitmap(sb, desc), block, count) ||
            in_range(ext4_inode_bitmap(sb, desc), block, count) ||
@@ -4694,6 +4736,7 @@ void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
                ext4_error(sb, "Adding blocks in system zones - "
                           "Block = %llu, count = %lu",
                           block, count);
+               err = -EINVAL;
                goto error_return;
        }
 
@@ -4762,7 +4805,7 @@ void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
 error_return:
        brelse(bitmap_bh);
        ext4_std_error(sb, err);
-       return;
+       return err;
 }
 
 /**
@@ -4782,6 +4825,8 @@ static void ext4_trim_extent(struct super_block *sb, int start, int count,
 {
        struct ext4_free_extent ex;
 
+       trace_ext4_trim_extent(sb, group, start, count);
+
        assert_spin_locked(ext4_group_lock_ptr(sb, group));
 
        ex.fe_start = start;
@@ -4802,7 +4847,7 @@ static void ext4_trim_extent(struct super_block *sb, int start, int count,
 /**
  * ext4_trim_all_free -- function to trim all free space in alloc. group
  * @sb:                        super block for file system
- * @e4b:               ext4 buddy
+ * @group:             group to be trimmed
  * @start:             first group block to examine
  * @max:               last group block to examine
  * @minblocks:         minimum extent block count
@@ -4823,10 +4868,12 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
                   ext4_grpblk_t minblocks)
 {
        void *bitmap;
-       ext4_grpblk_t next, count = 0;
+       ext4_grpblk_t next, count = 0, free_count = 0;
        struct ext4_buddy e4b;
        int ret;
 
+       trace_ext4_trim_all_free(sb, group, start, max);
+
        ret = ext4_mb_load_buddy(sb, group, &e4b);
        if (ret) {
                ext4_error(sb, "Error in loading buddy "
@@ -4836,6 +4883,10 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
        bitmap = e4b.bd_bitmap;
 
        ext4_lock_group(sb, group);
+       if (EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info) &&
+           minblocks >= atomic_read(&EXT4_SB(sb)->s_last_trim_minblks))
+               goto out;
+
        start = (e4b.bd_info->bb_first_free > start) ?
                e4b.bd_info->bb_first_free : start;
 
@@ -4850,6 +4901,7 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
                                         next - start, group, &e4b);
                        count += next - start;
                }
+               free_count += next - start;
                start = next + 1;
 
                if (fatal_signal_pending(current)) {
@@ -4863,9 +4915,13 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
                        ext4_lock_group(sb, group);
                }
 
-               if ((e4b.bd_info->bb_free - count) < minblocks)
+               if ((e4b.bd_info->bb_free - free_count) < minblocks)
                        break;
        }
+
+       if (!ret)
+               EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info);
+out:
        ext4_unlock_group(sb, group);
        ext4_mb_unload_buddy(&e4b);
 
@@ -4904,6 +4960,8 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
 
        if (unlikely(minlen > EXT4_BLOCKS_PER_GROUP(sb)))
                return -EINVAL;
+       if (start + len <= first_data_blk)
+               goto out;
        if (start < first_data_blk) {
                len -= first_data_blk - start;
                start = first_data_blk;
@@ -4952,5 +5010,9 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
        }
        range->len = trimmed * sb->s_blocksize;
 
+       if (!ret)
+               atomic_set(&EXT4_SB(sb)->s_last_trim_minblks, minlen);
+
+out:
        return ret;
 }
index 20b5e7bfebd175e27db63f959c0d5d89a9f929b0..9d4a636b546c529ac993b18e1fe6abb3e99ab9f5 100644 (file)
@@ -187,7 +187,6 @@ struct ext4_allocation_context {
        __u16 ac_flags;         /* allocation hints */
        __u8 ac_status;
        __u8 ac_criteria;
-       __u8 ac_repeats;
        __u8 ac_2order;         /* if request is to allocate 2^N blocks and
                                 * N > 0, the field stores N, otherwise 0 */
        __u8 ac_op;             /* operation, for history only */
index 8c9babac43dc941fa62ece2773f234044b8a5b7b..565a154e22d4bee058d8853cb02539e02509bc2f 100644 (file)
@@ -289,7 +289,7 @@ static struct stats dx_show_leaf(struct dx_hash_info *hinfo, struct ext4_dir_ent
                                while (len--) printk("%c", *name++);
                                ext4fs_dirhash(de->name, de->name_len, &h);
                                printk(":%x.%u ", h.hash,
-                                      ((char *) de - base));
+                                      (unsigned) ((char *) de - base));
                        }
                        space += EXT4_DIR_REC_LEN(de->name_len);
                        names++;
@@ -1013,7 +1013,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q
 
        *err = -ENOENT;
 errout:
-       dxtrace(printk(KERN_DEBUG "%s not found\n", name));
+       dxtrace(printk(KERN_DEBUG "%s not found\n", d_name->name));
        dx_release (frames);
        return NULL;
 }
@@ -1985,18 +1985,11 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
        if (!list_empty(&EXT4_I(inode)->i_orphan))
                goto out_unlock;
 
-       /* Orphan handling is only valid for files with data blocks
-        * being truncated, or files being unlinked. */
-
-       /* @@@ FIXME: Observation from aviro:
-        * I think I can trigger J_ASSERT in ext4_orphan_add().  We block
-        * here (on s_orphan_lock), so race with ext4_link() which might bump
-        * ->i_nlink. For, say it, character device. Not a regular file,
-        * not a directory, not a symlink and ->i_nlink > 0.
-        *
-        * tytso, 4/25/2009: I'm not sure how that could happen;
-        * shouldn't the fs core protect us from these sort of
-        * unlink()/link() races?
+       /*
+        * Orphan handling is only valid for files with data blocks
+        * being truncated, or files being unlinked. Note that we either
+        * hold i_mutex, or the inode can not be referenced from outside,
+        * so i_nlink should not be bumped due to race
         */
        J_ASSERT((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
                  S_ISLNK(inode->i_mode)) || inode->i_nlink == 0);
index 7bb8f76d470a019dd3801c6b9d8e6d76825f96c2..430c401d089514b6297d5ad88ea2d3c00b9dbb7b 100644 (file)
@@ -285,11 +285,7 @@ static int io_submit_init(struct ext4_io_submit *io,
        io_end = ext4_init_io_end(inode, GFP_NOFS);
        if (!io_end)
                return -ENOMEM;
-       do {
-               bio = bio_alloc(GFP_NOIO, nvecs);
-               nvecs >>= 1;
-       } while (bio == NULL);
-
+       bio = bio_alloc(GFP_NOIO, min(nvecs, BIO_MAX_PAGES));
        bio->bi_sector = bh->b_blocknr * (bh->b_size >> 9);
        bio->bi_bdev = bh->b_bdev;
        bio->bi_private = io->io_end = io_end;
index 80bbc9c60c247659047b805c9a74155bf2baeb14..707d3f16f7ce63732222e78c247a8a87e668b433 100644 (file)
 
 #include "ext4_jbd2.h"
 
+int ext4_resize_begin(struct super_block *sb)
+{
+       int ret = 0;
+
+       if (!capable(CAP_SYS_RESOURCE))
+               return -EPERM;
+
+       /*
+        * We are not allowed to do online-resizing on a filesystem mounted
+        * with error, because it can destroy the filesystem easily.
+        */
+       if (EXT4_SB(sb)->s_mount_state & EXT4_ERROR_FS) {
+               ext4_warning(sb, "There are errors in the filesystem, "
+                            "so online resizing is not allowed\n");
+               return -EPERM;
+       }
+
+       if (test_and_set_bit_lock(EXT4_RESIZING, &EXT4_SB(sb)->s_resize_flags))
+               ret = -EBUSY;
+
+       return ret;
+}
+
+void ext4_resize_end(struct super_block *sb)
+{
+       clear_bit_unlock(EXT4_RESIZING, &EXT4_SB(sb)->s_resize_flags);
+       smp_mb__after_clear_bit();
+}
+
 #define outside(b, first, last)        ((b) < (first) || (b) >= (last))
 #define inside(b, first, last) ((b) >= (first) && (b) < (last))
 
@@ -118,10 +147,8 @@ static struct buffer_head *bclean(handle_t *handle, struct super_block *sb,
                brelse(bh);
                bh = ERR_PTR(err);
        } else {
-               lock_buffer(bh);
                memset(bh->b_data, 0, sb->s_blocksize);
                set_buffer_uptodate(bh);
-               unlock_buffer(bh);
        }
 
        return bh;
@@ -132,8 +159,7 @@ static struct buffer_head *bclean(handle_t *handle, struct super_block *sb,
  * If that fails, restart the transaction & regain write access for the
  * buffer head which is used for block_bitmap modifications.
  */
-static int extend_or_restart_transaction(handle_t *handle, int thresh,
-                                        struct buffer_head *bh)
+static int extend_or_restart_transaction(handle_t *handle, int thresh)
 {
        int err;
 
@@ -144,9 +170,8 @@ static int extend_or_restart_transaction(handle_t *handle, int thresh,
        if (err < 0)
                return err;
        if (err) {
-               if ((err = ext4_journal_restart(handle, EXT4_MAX_TRANS_DATA)))
-                       return err;
-               if ((err = ext4_journal_get_write_access(handle, bh)))
+               err = ext4_journal_restart(handle, EXT4_MAX_TRANS_DATA);
+               if (err)
                        return err;
        }
 
@@ -181,21 +206,7 @@ static int setup_new_group_blocks(struct super_block *sb,
        if (IS_ERR(handle))
                return PTR_ERR(handle);
 
-       mutex_lock(&sbi->s_resize_lock);
-       if (input->group != sbi->s_groups_count) {
-               err = -EBUSY;
-               goto exit_journal;
-       }
-
-       if (IS_ERR(bh = bclean(handle, sb, input->block_bitmap))) {
-               err = PTR_ERR(bh);
-               goto exit_journal;
-       }
-
-       if (ext4_bg_has_super(sb, input->group)) {
-               ext4_debug("mark backup superblock %#04llx (+0)\n", start);
-               ext4_set_bit(0, bh->b_data);
-       }
+       BUG_ON(input->group != sbi->s_groups_count);
 
        /* Copy all of the GDT blocks into the backup in this group */
        for (i = 0, bit = 1, block = start + 1;
@@ -203,29 +214,26 @@ static int setup_new_group_blocks(struct super_block *sb,
                struct buffer_head *gdb;
 
                ext4_debug("update backup group %#04llx (+%d)\n", block, bit);
-
-               if ((err = extend_or_restart_transaction(handle, 1, bh)))
-                       goto exit_bh;
+               err = extend_or_restart_transaction(handle, 1);
+               if (err)
+                       goto exit_journal;
 
                gdb = sb_getblk(sb, block);
                if (!gdb) {
                        err = -EIO;
-                       goto exit_bh;
+                       goto exit_journal;
                }
                if ((err = ext4_journal_get_write_access(handle, gdb))) {
                        brelse(gdb);
-                       goto exit_bh;
+                       goto exit_journal;
                }
-               lock_buffer(gdb);
                memcpy(gdb->b_data, sbi->s_group_desc[i]->b_data, gdb->b_size);
                set_buffer_uptodate(gdb);
-               unlock_buffer(gdb);
                err = ext4_handle_dirty_metadata(handle, NULL, gdb);
                if (unlikely(err)) {
                        brelse(gdb);
-                       goto exit_bh;
+                       goto exit_journal;
                }
-               ext4_set_bit(bit, bh->b_data);
                brelse(gdb);
        }
 
@@ -235,9 +243,22 @@ static int setup_new_group_blocks(struct super_block *sb,
        err = sb_issue_zeroout(sb, gdblocks + start + 1, reserved_gdb,
                               GFP_NOFS);
        if (err)
-               goto exit_bh;
-       for (i = 0, bit = gdblocks + 1; i < reserved_gdb; i++, bit++)
-               ext4_set_bit(bit, bh->b_data);
+               goto exit_journal;
+
+       err = extend_or_restart_transaction(handle, 2);
+       if (err)
+               goto exit_journal;
+
+       bh = bclean(handle, sb, input->block_bitmap);
+       if (IS_ERR(bh)) {
+               err = PTR_ERR(bh);
+               goto exit_journal;
+       }
+
+       if (ext4_bg_has_super(sb, input->group)) {
+               ext4_debug("mark backup group tables %#04llx (+0)\n", start);
+               ext4_set_bits(bh->b_data, 0, gdblocks + reserved_gdb + 1);
+       }
 
        ext4_debug("mark block bitmap %#04llx (+%llu)\n", input->block_bitmap,
                   input->block_bitmap - start);
@@ -253,12 +274,9 @@ static int setup_new_group_blocks(struct super_block *sb,
        err = sb_issue_zeroout(sb, block, sbi->s_itb_per_group, GFP_NOFS);
        if (err)
                goto exit_bh;
-       for (i = 0, bit = input->inode_table - start;
-            i < sbi->s_itb_per_group; i++, bit++)
-               ext4_set_bit(bit, bh->b_data);
+       ext4_set_bits(bh->b_data, input->inode_table - start,
+                     sbi->s_itb_per_group);
 
-       if ((err = extend_or_restart_transaction(handle, 2, bh)))
-               goto exit_bh;
 
        ext4_mark_bitmap_end(input->blocks_count, sb->s_blocksize * 8,
                             bh->b_data);
@@ -285,7 +303,6 @@ exit_bh:
        brelse(bh);
 
 exit_journal:
-       mutex_unlock(&sbi->s_resize_lock);
        if ((err2 = ext4_journal_stop(handle)) && !err)
                err = err2;
 
@@ -377,15 +394,15 @@ static int verify_reserved_gdb(struct super_block *sb,
  * fail once we start modifying the data on disk, because JBD has no rollback.
  */
 static int add_new_gdb(handle_t *handle, struct inode *inode,
-                      struct ext4_new_group_data *input,
-                      struct buffer_head **primary)
+                      ext4_group_t group)
 {
        struct super_block *sb = inode->i_sb;
        struct ext4_super_block *es = EXT4_SB(sb)->s_es;
-       unsigned long gdb_num = input->group / EXT4_DESC_PER_BLOCK(sb);
+       unsigned long gdb_num = group / EXT4_DESC_PER_BLOCK(sb);
        ext4_fsblk_t gdblock = EXT4_SB(sb)->s_sbh->b_blocknr + 1 + gdb_num;
        struct buffer_head **o_group_desc, **n_group_desc;
        struct buffer_head *dind;
+       struct buffer_head *gdb_bh;
        int gdbackups;
        struct ext4_iloc iloc;
        __le32 *data;
@@ -408,11 +425,12 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
                return -EPERM;
        }
 
-       *primary = sb_bread(sb, gdblock);
-       if (!*primary)
+       gdb_bh = sb_bread(sb, gdblock);
+       if (!gdb_bh)
                return -EIO;
 
-       if ((gdbackups = verify_reserved_gdb(sb, *primary)) < 0) {
+       gdbackups = verify_reserved_gdb(sb, gdb_bh);
+       if (gdbackups < 0) {
                err = gdbackups;
                goto exit_bh;
        }
@@ -427,7 +445,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, "new group %u GDT block %llu not reserved",
-                            input->group, gdblock);
+                            group, gdblock);
                err = -EINVAL;
                goto exit_dind;
        }
@@ -436,7 +454,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
        if (unlikely(err))
                goto exit_dind;
 
-       err = ext4_journal_get_write_access(handle, *primary);
+       err = ext4_journal_get_write_access(handle, gdb_bh);
        if (unlikely(err))
                goto exit_sbh;
 
@@ -449,12 +467,13 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
        if (unlikely(err))
                goto exit_dindj;
 
-       n_group_desc = kmalloc((gdb_num + 1) * sizeof(struct buffer_head *),
-                       GFP_NOFS);
+       n_group_desc = ext4_kvmalloc((gdb_num + 1) *
+                                    sizeof(struct buffer_head *),
+                                    GFP_NOFS);
        if (!n_group_desc) {
                err = -ENOMEM;
-               ext4_warning(sb,
-                             "not enough memory for %lu groups", gdb_num + 1);
+               ext4_warning(sb, "not enough memory for %lu groups",
+                            gdb_num + 1);
                goto exit_inode;
        }
 
@@ -475,8 +494,8 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
        }
        inode->i_blocks -= (gdbackups + 1) * sb->s_blocksize >> 9;
        ext4_mark_iloc_dirty(handle, inode, &iloc);
-       memset((*primary)->b_data, 0, sb->s_blocksize);
-       err = ext4_handle_dirty_metadata(handle, NULL, *primary);
+       memset(gdb_bh->b_data, 0, sb->s_blocksize);
+       err = ext4_handle_dirty_metadata(handle, NULL, gdb_bh);
        if (unlikely(err)) {
                ext4_std_error(sb, err);
                goto exit_inode;
@@ -486,10 +505,10 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
        o_group_desc = EXT4_SB(sb)->s_group_desc;
        memcpy(n_group_desc, o_group_desc,
               EXT4_SB(sb)->s_gdb_count * sizeof(struct buffer_head *));
-       n_group_desc[gdb_num] = *primary;
+       n_group_desc[gdb_num] = gdb_bh;
        EXT4_SB(sb)->s_group_desc = n_group_desc;
        EXT4_SB(sb)->s_gdb_count++;
-       kfree(o_group_desc);
+       ext4_kvfree(o_group_desc);
 
        le16_add_cpu(&es->s_reserved_gdt_blocks, -1);
        err = ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh);
@@ -499,6 +518,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
        return err;
 
 exit_inode:
+       ext4_kvfree(n_group_desc);
        /* ext4_handle_release_buffer(handle, iloc.bh); */
        brelse(iloc.bh);
 exit_dindj:
@@ -508,7 +528,7 @@ exit_sbh:
 exit_dind:
        brelse(dind);
 exit_bh:
-       brelse(*primary);
+       brelse(gdb_bh);
 
        ext4_debug("leaving with error %d\n", err);
        return err;
@@ -528,7 +548,7 @@ exit_bh:
  * backup GDT blocks are stored in their reserved primary GDT block.
  */
 static int reserve_backup_gdb(handle_t *handle, struct inode *inode,
-                             struct ext4_new_group_data *input)
+                             ext4_group_t group)
 {
        struct super_block *sb = inode->i_sb;
        int reserved_gdb =le16_to_cpu(EXT4_SB(sb)->s_es->s_reserved_gdt_blocks);
@@ -599,7 +619,7 @@ static int reserve_backup_gdb(handle_t *handle, struct inode *inode,
         * Finally we can add each of the reserved backup GDT blocks from
         * the new group to its reserved primary GDT block.
         */
-       blk = input->group * EXT4_BLOCKS_PER_GROUP(sb);
+       blk = group * EXT4_BLOCKS_PER_GROUP(sb);
        for (i = 0; i < reserved_gdb; i++) {
                int err2;
                data = (__le32 *)primary[i]->b_data;
@@ -799,13 +819,6 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input)
                goto exit_put;
        }
 
-       mutex_lock(&sbi->s_resize_lock);
-       if (input->group != sbi->s_groups_count) {
-               ext4_warning(sb, "multiple resizers run on filesystem!");
-               err = -EBUSY;
-               goto exit_journal;
-       }
-
        if ((err = ext4_journal_get_write_access(handle, sbi->s_sbh)))
                goto exit_journal;
 
@@ -820,16 +833,25 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input)
                if ((err = ext4_journal_get_write_access(handle, primary)))
                        goto exit_journal;
 
-               if (reserved_gdb && ext4_bg_num_gdb(sb, input->group) &&
-                   (err = reserve_backup_gdb(handle, inode, input)))
+               if (reserved_gdb && ext4_bg_num_gdb(sb, input->group)) {
+                       err = reserve_backup_gdb(handle, inode, input->group);
+                       if (err)
+                               goto exit_journal;
+               }
+       } else {
+               /*
+                * Note that we can access new group descriptor block safely
+                * only if add_new_gdb() succeeds.
+                */
+               err = add_new_gdb(handle, inode, input->group);
+               if (err)
                        goto exit_journal;
-       } else if ((err = add_new_gdb(handle, inode, input, &primary)))
-               goto exit_journal;
+               primary = sbi->s_group_desc[gdb_num];
+       }
 
         /*
          * OK, now we've set up the new group.  Time to make it active.
          *
-         * We do not lock all allocations via s_resize_lock
          * so we have to be safe wrt. concurrent accesses the group
          * data.  So we need to be careful to set all of the relevant
          * group descriptor data etc. *before* we enable the group.
@@ -886,13 +908,9 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input)
         *
         * The precise rules we use are:
         *
-        * * Writers of s_groups_count *must* hold s_resize_lock
-        * AND
         * * Writers must perform a smp_wmb() after updating all dependent
         *   data and before modifying the groups count
         *
-        * * Readers must hold s_resize_lock over the access
-        * OR
         * * Readers must perform an smp_rmb() after reading the groups count
         *   and before reading any dependent data.
         *
@@ -937,10 +955,9 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input)
        ext4_handle_dirty_super(handle, sb);
 
 exit_journal:
-       mutex_unlock(&sbi->s_resize_lock);
        if ((err2 = ext4_journal_stop(handle)) && !err)
                err = err2;
-       if (!err) {
+       if (!err && primary) {
                update_backups(sb, sbi->s_sbh->b_blocknr, (char *)es,
                               sizeof(struct ext4_super_block));
                update_backups(sb, primary->b_blocknr, primary->b_data,
@@ -969,16 +986,13 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es,
        ext4_grpblk_t add;
        struct buffer_head *bh;
        handle_t *handle;
-       int err;
+       int err, err2;
        ext4_group_t group;
 
-       /* We don't need to worry about locking wrt other resizers just
-        * yet: we're going to revalidate es->s_blocks_count after
-        * taking the s_resize_lock below. */
        o_blocks_count = ext4_blocks_count(es);
 
        if (test_opt(sb, DEBUG))
-               printk(KERN_DEBUG "EXT4-fs: extending last group from %llu uto %llu blocks\n",
+               printk(KERN_DEBUG "EXT4-fs: extending last group from %llu to %llu blocks\n",
                       o_blocks_count, n_blocks_count);
 
        if (n_blocks_count == 0 || n_blocks_count == o_blocks_count)
@@ -995,7 +1009,7 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es,
 
        if (n_blocks_count < o_blocks_count) {
                ext4_warning(sb, "can't shrink FS - resize aborted");
-               return -EBUSY;
+               return -EINVAL;
        }
 
        /* Handle the remaining blocks in the last group only. */
@@ -1038,32 +1052,25 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es,
                goto exit_put;
        }
 
-       mutex_lock(&EXT4_SB(sb)->s_resize_lock);
-       if (o_blocks_count != ext4_blocks_count(es)) {
-               ext4_warning(sb, "multiple resizers run on filesystem!");
-               mutex_unlock(&EXT4_SB(sb)->s_resize_lock);
-               ext4_journal_stop(handle);
-               err = -EBUSY;
-               goto exit_put;
-       }
-
        if ((err = ext4_journal_get_write_access(handle,
                                                 EXT4_SB(sb)->s_sbh))) {
                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;
        }
        ext4_blocks_count_set(es, o_blocks_count + add);
-       mutex_unlock(&EXT4_SB(sb)->s_resize_lock);
        ext4_debug("freeing blocks %llu through %llu\n", o_blocks_count,
                   o_blocks_count + add);
        /* We add the blocks to the bitmap and set the group need init bit */
-       ext4_add_groupblocks(handle, sb, o_blocks_count, add);
+       err = ext4_group_add_blocks(handle, sb, o_blocks_count, add);
        ext4_handle_dirty_super(handle, sb);
        ext4_debug("freed blocks %llu through %llu\n", o_blocks_count,
                   o_blocks_count + add);
-       if ((err = ext4_journal_stop(handle)))
+       err2 = ext4_journal_stop(handle);
+       if (!err && err2)
+               err = err2;
+
+       if (err)
                goto exit_put;
 
        if (test_opt(sb, DEBUG))
index 9ea71aa864b3a620667aed2c7fbbe5348341d312..4687fea0c00f24498ff58c80e7d39d9c086bd62a 100644 (file)
@@ -110,6 +110,35 @@ static struct file_system_type ext3_fs_type = {
 #define IS_EXT3_SB(sb) (0)
 #endif
 
+void *ext4_kvmalloc(size_t size, gfp_t flags)
+{
+       void *ret;
+
+       ret = kmalloc(size, flags);
+       if (!ret)
+               ret = __vmalloc(size, flags, PAGE_KERNEL);
+       return ret;
+}
+
+void *ext4_kvzalloc(size_t size, gfp_t flags)
+{
+       void *ret;
+
+       ret = kzalloc(size, flags);
+       if (!ret)
+               ret = __vmalloc(size, flags | __GFP_ZERO, PAGE_KERNEL);
+       return ret;
+}
+
+void ext4_kvfree(void *ptr)
+{
+       if (is_vmalloc_addr(ptr))
+               vfree(ptr);
+       else
+               kfree(ptr);
+
+}
+
 ext4_fsblk_t ext4_block_bitmap(struct super_block *sb,
                               struct ext4_group_desc *bg)
 {
@@ -269,6 +298,7 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
        journal_t *journal;
        handle_t  *handle;
 
+       trace_ext4_journal_start(sb, nblocks, _RET_IP_);
        if (sb->s_flags & MS_RDONLY)
                return ERR_PTR(-EROFS);
 
@@ -789,11 +819,8 @@ static void ext4_put_super(struct super_block *sb)
 
        for (i = 0; i < sbi->s_gdb_count; i++)
                brelse(sbi->s_group_desc[i]);
-       kfree(sbi->s_group_desc);
-       if (is_vmalloc_addr(sbi->s_flex_groups))
-               vfree(sbi->s_flex_groups);
-       else
-               kfree(sbi->s_flex_groups);
+       ext4_kvfree(sbi->s_group_desc);
+       ext4_kvfree(sbi->s_flex_groups);
        percpu_counter_destroy(&sbi->s_freeblocks_counter);
        percpu_counter_destroy(&sbi->s_freeinodes_counter);
        percpu_counter_destroy(&sbi->s_dirs_counter);
@@ -1976,15 +2003,11 @@ static int ext4_fill_flex_info(struct super_block *sb)
                        ((le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) + 1) <<
                              EXT4_DESC_PER_BLOCK_BITS(sb))) / groups_per_flex;
        size = flex_group_count * sizeof(struct flex_groups);
-       sbi->s_flex_groups = kzalloc(size, GFP_KERNEL);
+       sbi->s_flex_groups = ext4_kvzalloc(size, GFP_KERNEL);
        if (sbi->s_flex_groups == NULL) {
-               sbi->s_flex_groups = vzalloc(size);
-               if (sbi->s_flex_groups == NULL) {
-                       ext4_msg(sb, KERN_ERR,
-                                "not enough memory for %u flex groups",
-                                flex_group_count);
-                       goto failed;
-               }
+               ext4_msg(sb, KERN_ERR, "not enough memory for %u flex groups",
+                        flex_group_count);
+               goto failed;
        }
 
        for (i = 0; i < sbi->s_groups_count; i++) {
@@ -2383,17 +2406,25 @@ static unsigned long ext4_get_stripe_size(struct ext4_sb_info *sbi)
        unsigned long stride = le16_to_cpu(sbi->s_es->s_raid_stride);
        unsigned long stripe_width =
                        le32_to_cpu(sbi->s_es->s_raid_stripe_width);
+       int ret;
 
        if (sbi->s_stripe && sbi->s_stripe <= sbi->s_blocks_per_group)
-               return sbi->s_stripe;
-
-       if (stripe_width <= sbi->s_blocks_per_group)
-               return stripe_width;
+               ret = sbi->s_stripe;
+       else if (stripe_width <= sbi->s_blocks_per_group)
+               ret = stripe_width;
+       else if (stride <= sbi->s_blocks_per_group)
+               ret = stride;
+       else
+               ret = 0;
 
-       if (stride <= sbi->s_blocks_per_group)
-               return stride;
+       /*
+        * If the stripe width is 1, this makes no sense and
+        * we set it to 0 to turn off stripe handling code.
+        */
+       if (ret <= 1)
+               ret = 0;
 
-       return 0;
+       return ret;
 }
 
 /* sysfs supprt */
@@ -3408,8 +3439,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                        (EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb)));
        db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) /
                   EXT4_DESC_PER_BLOCK(sb);
-       sbi->s_group_desc = kmalloc(db_count * sizeof(struct buffer_head *),
-                                   GFP_KERNEL);
+       sbi->s_group_desc = ext4_kvmalloc(db_count *
+                                         sizeof(struct buffer_head *),
+                                         GFP_KERNEL);
        if (sbi->s_group_desc == NULL) {
                ext4_msg(sb, KERN_ERR, "not enough memory");
                goto failed_mount;
@@ -3491,7 +3523,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 
        INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */
        mutex_init(&sbi->s_orphan_lock);
-       mutex_init(&sbi->s_resize_lock);
+       sbi->s_resize_flags = 0;
 
        sb->s_root = NULL;
 
@@ -3741,12 +3773,8 @@ failed_mount_wq:
        }
 failed_mount3:
        del_timer(&sbi->s_err_report);
-       if (sbi->s_flex_groups) {
-               if (is_vmalloc_addr(sbi->s_flex_groups))
-                       vfree(sbi->s_flex_groups);
-               else
-                       kfree(sbi->s_flex_groups);
-       }
+       if (sbi->s_flex_groups)
+               ext4_kvfree(sbi->s_flex_groups);
        percpu_counter_destroy(&sbi->s_freeblocks_counter);
        percpu_counter_destroy(&sbi->s_freeinodes_counter);
        percpu_counter_destroy(&sbi->s_dirs_counter);
@@ -3756,7 +3784,7 @@ failed_mount3:
 failed_mount2:
        for (i = 0; i < db_count; i++)
                brelse(sbi->s_group_desc[i]);
-       kfree(sbi->s_group_desc);
+       ext4_kvfree(sbi->s_group_desc);
 failed_mount:
        if (sbi->s_proc) {
                remove_proc_entry(sb->s_id, ext4_proc_root);
diff --git a/fs/ext4/truncate.h b/fs/ext4/truncate.h
new file mode 100644 (file)
index 0000000..011ba66
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * linux/fs/ext4/truncate.h
+ *
+ * Common inline functions needed for truncate support
+ */
+
+/*
+ * Truncate blocks that were not used by write. We have to truncate the
+ * pagecache as well so that corresponding buffers get properly unmapped.
+ */
+static inline void ext4_truncate_failed_write(struct inode *inode)
+{
+       truncate_inode_pages(inode->i_mapping, inode->i_size);
+       ext4_truncate(inode);
+}
+
+/*
+ * Work out how many blocks we need to proceed with the next chunk of a
+ * truncate transaction.
+ */
+static inline unsigned long ext4_blocks_for_truncate(struct inode *inode)
+{
+       ext4_lblk_t needed;
+
+       needed = inode->i_blocks >> (inode->i_sb->s_blocksize_bits - 9);
+
+       /* Give ourselves just enough room to cope with inodes in which
+        * i_blocks is corrupt: we've seen disk corruptions in the past
+        * which resulted in random data in an inode which looked enough
+        * like a regular file for ext4 to try to delete it.  Things
+        * will go a bit crazy if that happens, but at least we should
+        * try not to panic the whole kernel. */
+       if (needed < 2)
+               needed = 2;
+
+       /* But we need to bound the transaction so we don't overflow the
+        * journal. */
+       if (needed > EXT4_MAX_TRANS_DATA)
+               needed = EXT4_MAX_TRANS_DATA;
+
+       return EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + needed;
+}
+
index d5e33a077a67e7bb7cce426b5a4c5616e201c686..d0dddaceac594888c96388b5ed82f7ab33bc9846 100644 (file)
@@ -82,18 +82,14 @@ generic_acl_set(struct dentry *dentry, const char *name, const void *value,
                        return PTR_ERR(acl);
        }
        if (acl) {
-               mode_t mode;
-
                error = posix_acl_valid(acl);
                if (error)
                        goto failed;
                switch (type) {
                case ACL_TYPE_ACCESS:
-                       mode = inode->i_mode;
-                       error = posix_acl_equiv_mode(acl, &mode);
+                       error = posix_acl_equiv_mode(acl, &inode->i_mode);
                        if (error < 0)
                                goto failed;
-                       inode->i_mode = mode;
                        inode->i_ctime = CURRENT_TIME;
                        if (error == 0) {
                                posix_acl_release(acl);
@@ -125,21 +121,20 @@ int
 generic_acl_init(struct inode *inode, struct inode *dir)
 {
        struct posix_acl *acl = NULL;
-       mode_t mode = inode->i_mode;
        int error;
 
-       inode->i_mode = mode & ~current_umask();
        if (!S_ISLNK(inode->i_mode))
                acl = get_cached_acl(dir, ACL_TYPE_DEFAULT);
        if (acl) {
                if (S_ISDIR(inode->i_mode))
                        set_cached_acl(inode, ACL_TYPE_DEFAULT, acl);
-               error = posix_acl_create(&acl, GFP_KERNEL, &mode);
+               error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode);
                if (error < 0)
                        return error;
-               inode->i_mode = mode;
                if (error > 0)
                        set_cached_acl(inode, ACL_TYPE_ACCESS, acl);
+       } else {
+               inode->i_mode &= ~current_umask();
        }
        error = 0;
 
index 884c9af0542fac0658cf5108d7869e5a08668e5a..34501b64bc47a416993677b641b30fe64a84d13c 100644 (file)
@@ -72,7 +72,7 @@ struct posix_acl *gfs2_get_acl(struct inode *inode, int type)
        return gfs2_acl_get(GFS2_I(inode), type);
 }
 
-static int gfs2_set_mode(struct inode *inode, mode_t mode)
+static int gfs2_set_mode(struct inode *inode, umode_t mode)
 {
        int error = 0;
 
@@ -117,7 +117,7 @@ int gfs2_acl_create(struct gfs2_inode *dip, struct inode *inode)
 {
        struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
        struct posix_acl *acl;
-       mode_t mode = inode->i_mode;
+       umode_t mode = inode->i_mode;
        int error = 0;
 
        if (!sdp->sd_args.ar_posix_acl)
@@ -276,7 +276,7 @@ static int gfs2_xattr_system_set(struct dentry *dentry, const char *name,
                goto out_release;
 
        if (type == ACL_TYPE_ACCESS) {
-               mode_t mode = inode->i_mode;
+               umode_t mode = inode->i_mode;
                error = posix_acl_equiv_mode(acl, &mode);
 
                if (error <= 0) {
index 8635be5ffd97e90f443efeb09acdc6efa4d4cf39..970ea987b3f61b28a99016a5a8d596b429685c42 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/statfs.h>
 #include <linux/types.h>
 #include <linux/pid_namespace.h>
+#include <linux/namei.h>
 #include <asm/uaccess.h>
 #include "os.h"
 
index d0c72ff6b30e6d259c80d4cd28197441f141fe43..5aab80dc008cf681e64f47a32759055bc013b13f 100644 (file)
@@ -399,12 +399,12 @@ void __insert_inode_hash(struct inode *inode, unsigned long hashval)
 EXPORT_SYMBOL(__insert_inode_hash);
 
 /**
- *     remove_inode_hash - remove an inode from the hash
+ *     __remove_inode_hash - remove an inode from the hash
  *     @inode: inode to unhash
  *
  *     Remove an inode from the superblock.
  */
-void remove_inode_hash(struct inode *inode)
+void __remove_inode_hash(struct inode *inode)
 {
        spin_lock(&inode_hash_lock);
        spin_lock(&inode->i_lock);
@@ -412,7 +412,7 @@ void remove_inode_hash(struct inode *inode)
        spin_unlock(&inode->i_lock);
        spin_unlock(&inode_hash_lock);
 }
-EXPORT_SYMBOL(remove_inode_hash);
+EXPORT_SYMBOL(__remove_inode_hash);
 
 void end_writeback(struct inode *inode)
 {
@@ -454,7 +454,9 @@ static void evict(struct inode *inode)
        BUG_ON(!(inode->i_state & I_FREEING));
        BUG_ON(!list_empty(&inode->i_lru));
 
-       inode_wb_list_del(inode);
+       if (!list_empty(&inode->i_wb_list))
+               inode_wb_list_del(inode);
+
        inode_sb_list_del(inode);
 
        if (op->evict_inode) {
@@ -1328,7 +1330,8 @@ static void iput_final(struct inode *inode)
        }
 
        inode->i_state |= I_FREEING;
-       inode_lru_list_del(inode);
+       if (!list_empty(&inode->i_lru))
+               inode_lru_list_del(inode);
        spin_unlock(&inode->i_lock);
 
        evict(inode);
index 2c62c5aae82ff8936ae30036fe595aff68ab6877..16a698bd906d7f5e0354475be1eabad998ce771f 100644 (file)
@@ -257,9 +257,12 @@ static void
 __flush_batch(journal_t *journal, int *batch_count)
 {
        int i;
+       struct blk_plug plug;
 
+       blk_start_plug(&plug);
        for (i = 0; i < *batch_count; i++)
-               write_dirty_buffer(journal->j_chkpt_bhs[i], WRITE);
+               write_dirty_buffer(journal->j_chkpt_bhs[i], WRITE_SYNC);
+       blk_finish_plug(&plug);
 
        for (i = 0; i < *batch_count; i++) {
                struct buffer_head *bh = journal->j_chkpt_bhs[i];
index 0dfa5b598e68fa3f358f043c442eb39558bf6f52..f24df13adc4e9cfb5d71d851c959193e0f1e2b61 100644 (file)
@@ -2390,73 +2390,6 @@ static void __exit journal_exit(void)
        jbd2_journal_destroy_caches();
 }
 
-/* 
- * jbd2_dev_to_name is a utility function used by the jbd2 and ext4 
- * tracing infrastructure to map a dev_t to a device name.
- *
- * The caller should use rcu_read_lock() in order to make sure the
- * device name stays valid until its done with it.  We use
- * rcu_read_lock() as well to make sure we're safe in case the caller
- * gets sloppy, and because rcu_read_lock() is cheap and can be safely
- * nested.
- */
-struct devname_cache {
-       struct rcu_head rcu;
-       dev_t           device;
-       char            devname[BDEVNAME_SIZE];
-};
-#define CACHE_SIZE_BITS 6
-static struct devname_cache *devcache[1 << CACHE_SIZE_BITS];
-static DEFINE_SPINLOCK(devname_cache_lock);
-
-static void free_devcache(struct rcu_head *rcu)
-{
-       kfree(rcu);
-}
-
-const char *jbd2_dev_to_name(dev_t device)
-{
-       int     i = hash_32(device, CACHE_SIZE_BITS);
-       char    *ret;
-       struct block_device *bd;
-       static struct devname_cache *new_dev;
-
-       rcu_read_lock();
-       if (devcache[i] && devcache[i]->device == device) {
-               ret = devcache[i]->devname;
-               rcu_read_unlock();
-               return ret;
-       }
-       rcu_read_unlock();
-
-       new_dev = kmalloc(sizeof(struct devname_cache), GFP_KERNEL);
-       if (!new_dev)
-               return "NODEV-ALLOCFAILURE"; /* Something non-NULL */
-       bd = bdget(device);
-       spin_lock(&devname_cache_lock);
-       if (devcache[i]) {
-               if (devcache[i]->device == device) {
-                       kfree(new_dev);
-                       bdput(bd);
-                       ret = devcache[i]->devname;
-                       spin_unlock(&devname_cache_lock);
-                       return ret;
-               }
-               call_rcu(&devcache[i]->rcu, free_devcache);
-       }
-       devcache[i] = new_dev;
-       devcache[i]->device = device;
-       if (bd) {
-               bdevname(bd, devcache[i]->devname);
-               bdput(bd);
-       } else
-               __bdevname(device, devcache[i]->devname);
-       ret = devcache[i]->devname;
-       spin_unlock(&devname_cache_lock);
-       return ret;
-}
-EXPORT_SYMBOL(jbd2_dev_to_name);
-
 MODULE_LICENSE("GPL");
 module_init(journal_init);
 module_exit(journal_exit);
index 27c511a1cf053f3dbbae46e76588af66f2bff7aa..926d02068a142c6ac4b5ac4cb7f02fec0b666937 100644 (file)
@@ -227,7 +227,7 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
        case ACL_TYPE_ACCESS:
                xprefix = JFFS2_XPREFIX_ACL_ACCESS;
                if (acl) {
-                       mode_t mode = inode->i_mode;
+                       umode_t mode = inode->i_mode;
                        rc = posix_acl_equiv_mode(acl, &mode);
                        if (rc < 0)
                                return rc;
@@ -259,7 +259,7 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
        return rc;
 }
 
-int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, mode_t *i_mode)
+int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, umode_t *i_mode)
 {
        struct posix_acl *acl;
        int rc;
index b3421c78d9f85482be72d045fdde7194e2bb63d8..9b477246f2a6ec1c0e0f0b9a34c870f44e6ba734 100644 (file)
@@ -28,7 +28,7 @@ struct jffs2_acl_header {
 
 struct posix_acl *jffs2_get_acl(struct inode *inode, int type);
 extern int jffs2_acl_chmod(struct inode *);
-extern int jffs2_init_acl_pre(struct inode *, struct inode *, mode_t *);
+extern int jffs2_init_acl_pre(struct inode *, struct inode *, umode_t *);
 extern int jffs2_init_acl_post(struct inode *);
 
 extern const struct xattr_handler jffs2_acl_access_xattr_handler;
index b81b35ddf4e47bd75b321c2c471dada1fcbad109..bbcb9755dd2b6c85b2df88486622dca7d817951a 100644 (file)
@@ -406,7 +406,7 @@ int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
 
 /* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
    fill in the raw_inode while you're at it. */
-struct inode *jffs2_new_inode (struct inode *dir_i, mode_t mode, struct jffs2_raw_inode *ri)
+struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_raw_inode *ri)
 {
        struct inode *inode;
        struct super_block *sb = dir_i->i_sb;
index 526979c607b6b91e5354b16e35f825aa0ef21d11..6c1755c59c0ff9975a331d3217f751e274b221a0 100644 (file)
@@ -173,7 +173,7 @@ int jffs2_do_setattr (struct inode *, struct iattr *);
 struct inode *jffs2_iget(struct super_block *, unsigned long);
 void jffs2_evict_inode (struct inode *);
 void jffs2_dirty_inode(struct inode *inode, int flags);
-struct inode *jffs2_new_inode (struct inode *dir_i, mode_t mode,
+struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode,
                               struct jffs2_raw_inode *ri);
 int jffs2_statfs (struct dentry *, struct kstatfs *);
 int jffs2_remount_fs (struct super_block *, int *, char *);
index b3a32caf2b4596d8ce0fa8a53c5084787088c4cc..45559dc3ea2f59297f57c523a8eac2b3f40c06c6 100644 (file)
@@ -127,16 +127,14 @@ int jfs_init_acl(tid_t tid, struct inode *inode, struct inode *dir)
                return PTR_ERR(acl);
 
        if (acl) {
-               mode_t mode = inode->i_mode;
                if (S_ISDIR(inode->i_mode)) {
                        rc = jfs_set_acl(tid, inode, ACL_TYPE_DEFAULT, acl);
                        if (rc)
                                goto cleanup;
                }
-               rc = posix_acl_create(&acl, GFP_KERNEL, &mode);
+               rc = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode);
                if (rc < 0)
                        goto cleanup; /* posix_acl_release(NULL) is no-op */
-               inode->i_mode = mode;
                if (rc > 0)
                        rc = jfs_set_acl(tid, inode, ACL_TYPE_ACCESS, acl);
 cleanup:
index 24838f1eeee5c0f5093d96fef01c80d1835991ea..e87fedef23db7938aa7b38f64f385b0615e193b4 100644 (file)
@@ -693,8 +693,7 @@ static int can_set_system_xattr(struct inode *inode, const char *name,
                        return rc;
                }
                if (acl) {
-                       mode_t mode = inode->i_mode;
-                       rc = posix_acl_equiv_mode(acl, &mode);
+                       rc = posix_acl_equiv_mode(acl, &inode->i_mode);
                        posix_acl_release(acl);
                        if (rc < 0) {
                                printk(KERN_ERR
@@ -702,7 +701,6 @@ static int can_set_system_xattr(struct inode *inode, const char *name,
                                       rc);
                                return rc;
                        }
-                       inode->i_mode = mode;
                        mark_inode_dirty(inode);
                }
                /*
index f8c69d37379348973d2a12e055dbc6c61dc4cbf4..3d607bd80e09f683fac132d6387f152c1df11f4c 100644 (file)
@@ -179,19 +179,14 @@ static int check_acl(struct inode *inode, int mask)
 #ifdef CONFIG_FS_POSIX_ACL
        struct posix_acl *acl;
 
-       /*
-        * Under RCU walk, we cannot even do a "get_cached_acl()",
-        * because that involves locking and getting a refcount on
-        * a cached ACL.
-        *
-        * So the only case we handle during RCU walking is the
-        * case of a cached "no ACL at all", which needs no locks
-        * or refcounts.
-        */
        if (mask & MAY_NOT_BLOCK) {
-               if (negative_cached_acl(inode, ACL_TYPE_ACCESS))
+               acl = get_cached_acl_rcu(inode, ACL_TYPE_ACCESS);
+               if (!acl)
                        return -EAGAIN;
-               return -ECHILD;
+               /* no ->get_acl() calls in RCU mode... */
+               if (acl == ACL_NOT_CACHED)
+                       return -ECHILD;
+               return posix_acl_permission(inode, acl, mask);
        }
 
        acl = get_cached_acl(inode, ACL_TYPE_ACCESS);
@@ -716,19 +711,25 @@ static int follow_automount(struct path *path, unsigned flags,
        if ((flags & LOOKUP_NO_AUTOMOUNT) && !(flags & LOOKUP_PARENT))
                return -EISDIR; /* we actually want to stop here */
 
-       /* We want to mount if someone is trying to open/create a file of any
-        * type under the mountpoint, wants to traverse through the mountpoint
-        * or wants to open the mounted directory.
-        *
+       /*
         * We don't want to mount if someone's just doing a stat and they've
         * set AT_SYMLINK_NOFOLLOW - unless they're stat'ing a directory and
         * appended a '/' to the name.
         */
-       if (!(flags & LOOKUP_FOLLOW) &&
-           !(flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY |
-                      LOOKUP_OPEN | LOOKUP_CREATE)))
-               return -EISDIR;
-
+       if (!(flags & LOOKUP_FOLLOW)) {
+               /* We do, however, want to mount if someone wants to open or
+                * create a file of any type under the mountpoint, wants to
+                * traverse through the mountpoint or wants to open the mounted
+                * directory.
+                * Also, autofs may mark negative dentries as being automount
+                * points.  These will need the attentions of the daemon to
+                * instantiate them before they can be used.
+                */
+               if (!(flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY |
+                            LOOKUP_OPEN | LOOKUP_CREATE)) &&
+                   path->dentry->d_inode)
+                       return -EISDIR;
+       }
        current->total_link_count++;
        if (current->total_link_count >= 40)
                return -ELOOP;
index e49e73107e6290dda0f9bfa5d625edf5fc54ec2b..7ef23979896dd2cffdbc43aa8b0a5a9e3aaecb09 100644 (file)
@@ -415,7 +415,7 @@ fail:
 }
 
 int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode,
-               mode_t mode)
+               umode_t mode)
 {
        struct posix_acl *dfacl, *acl;
        int error = 0;
index 38053d823eb061060cdccdd904ebe9f830ee0c7f..85f1690ca08c110bcd1c1754e564f23619295a1a 100644 (file)
@@ -316,7 +316,7 @@ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                 int flags, struct nfs_open_context *ctx)
 {
        struct nfs3_createdata *data;
-       mode_t mode = sattr->ia_mode;
+       umode_t mode = sattr->ia_mode;
        int status = -ENOMEM;
 
        dprintk("NFS call  create %s\n", dentry->d_name.name);
@@ -562,7 +562,7 @@ static int
 nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr)
 {
        struct nfs3_createdata *data;
-       int mode = sattr->ia_mode;
+       umode_t mode = sattr->ia_mode;
        int status = -ENOMEM;
 
        dprintk("NFS call  mkdir %s\n", dentry->d_name.name);
@@ -681,7 +681,7 @@ nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                dev_t rdev)
 {
        struct nfs3_createdata *data;
-       mode_t mode = sattr->ia_mode;
+       umode_t mode = sattr->ia_mode;
        int status = -ENOMEM;
 
        dprintk("NFS call  mknod %s %u:%u\n", dentry->d_name.name,
index 783c58d9daf11b92898676b2af2f55765cdd3caf..a7219075b4deddcd7881a9b3a5c1586f731d36f2 100644 (file)
@@ -247,7 +247,7 @@ static int ocfs2_set_acl(handle_t *handle,
        case ACL_TYPE_ACCESS:
                name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
                if (acl) {
-                       mode_t mode = inode->i_mode;
+                       umode_t mode = inode->i_mode;
                        ret = posix_acl_equiv_mode(acl, &mode);
                        if (ret < 0)
                                return ret;
@@ -351,7 +351,7 @@ int ocfs2_init_acl(handle_t *handle,
        struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
        struct posix_acl *acl = NULL;
        int ret = 0, ret2;
-       mode_t mode;
+       umode_t mode;
 
        if (!S_ISLNK(inode->i_mode)) {
                if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
index d43729a760e26d1a6acc1024fa33fb2efdaa5d33..10027b42b7e29aefa1b9158ab0ec23de5f00914a 100644 (file)
@@ -149,10 +149,10 @@ posix_acl_valid(const struct posix_acl *acl)
  * file mode permission bits, or else 1. Returns -E... on error.
  */
 int
-posix_acl_equiv_mode(const struct posix_acl *acl, mode_t *mode_p)
+posix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p)
 {
        const struct posix_acl_entry *pa, *pe;
-       mode_t mode = 0;
+       umode_t mode = 0;
        int not_equiv = 0;
 
        FOREACH_ACL_ENTRY(pa, acl, pe) {
@@ -188,7 +188,7 @@ posix_acl_equiv_mode(const struct posix_acl *acl, mode_t *mode_p)
  * Create an ACL representing the file mode permission bits of an inode.
  */
 struct posix_acl *
-posix_acl_from_mode(mode_t mode, gfp_t flags)
+posix_acl_from_mode(umode_t mode, gfp_t flags)
 {
        struct posix_acl *acl = posix_acl_alloc(3, flags);
        if (!acl)
@@ -279,11 +279,11 @@ check_perm:
  * system calls. All permissions that are not granted by the acl are removed.
  * The permissions in the acl are changed to reflect the mode_p parameter.
  */
-static int posix_acl_create_masq(struct posix_acl *acl, mode_t *mode_p)
+static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
 {
        struct posix_acl_entry *pa, *pe;
        struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
-       mode_t mode = *mode_p;
+       umode_t mode = *mode_p;
        int not_equiv = 0;
 
        /* assert(atomic_read(acl->a_refcount) == 1); */
@@ -336,7 +336,7 @@ static int posix_acl_create_masq(struct posix_acl *acl, mode_t *mode_p)
 /*
  * Modify the ACL for the chmod syscall.
  */
-static int posix_acl_chmod_masq(struct posix_acl *acl, mode_t mode)
+static int posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode)
 {
        struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
        struct posix_acl_entry *pa, *pe;
@@ -382,7 +382,7 @@ static int posix_acl_chmod_masq(struct posix_acl *acl, mode_t mode)
 }
 
 int
-posix_acl_create(struct posix_acl **acl, gfp_t gfp, mode_t *mode_p)
+posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
 {
        struct posix_acl *clone = posix_acl_clone(*acl, gfp);
        int err = -ENOMEM;
@@ -400,7 +400,7 @@ posix_acl_create(struct posix_acl **acl, gfp_t gfp, mode_t *mode_p)
 EXPORT_SYMBOL(posix_acl_create);
 
 int
-posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, mode_t mode)
+posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
 {
        struct posix_acl *clone = posix_acl_clone(*acl, gfp);
        int err = -ENOMEM;
index 08e3eccf9a12071ae2007a076729851f5c431dbd..5eb02069e1b817730050b7b46304a9244c064bde 100644 (file)
@@ -1118,7 +1118,7 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf,
         * Warn that /proc/pid/oom_adj is deprecated, see
         * Documentation/feature-removal-schedule.txt.
         */
-       WARN_ONCE(1, "%s (%d): /proc/%d/oom_adj is deprecated, please use /proc/%d/oom_score_adj instead.\n",
+       printk_once(KERN_WARNING "%s (%d): /proc/%d/oom_adj is deprecated, please use /proc/%d/oom_score_adj instead.\n",
                  current->comm, task_pid_nr(current), task_pid_nr(task),
                  task_pid_nr(task));
        task->signal->oom_adj = oom_adjust;
@@ -1919,6 +1919,14 @@ static int proc_fd_info(struct inode *inode, struct path *path, char *info)
                spin_lock(&files->file_lock);
                file = fcheck_files(files, fd);
                if (file) {
+                       unsigned int f_flags;
+                       struct fdtable *fdt;
+
+                       fdt = files_fdtable(files);
+                       f_flags = file->f_flags & ~O_CLOEXEC;
+                       if (FD_ISSET(fd, fdt->close_on_exec))
+                               f_flags |= O_CLOEXEC;
+
                        if (path) {
                                *path = file->f_path;
                                path_get(&file->f_path);
@@ -1928,7 +1936,7 @@ static int proc_fd_info(struct inode *inode, struct path *path, char *info)
                                         "pos:\t%lli\n"
                                         "flags:\t0%o\n",
                                         (long long) file->f_pos,
-                                        file->f_flags);
+                                        f_flags);
                        spin_unlock(&files->file_lock);
                        put_files_struct(files);
                        return 0;
index 977ed272384574e0220bdfe06439ea3cdd045ad5..893b961dcfd8b45219a9266ca4db78ee9b44fb71 100644 (file)
@@ -39,8 +39,9 @@
 #define        PSTORE_NAMELEN  64
 
 struct pstore_private {
+       struct pstore_info *psi;
+       enum pstore_type_id type;
        u64     id;
-       int     (*erase)(u64);
        ssize_t size;
        char    data[];
 };
@@ -73,7 +74,7 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry)
 {
        struct pstore_private *p = dentry->d_inode->i_private;
 
-       p->erase(p->id);
+       p->psi->erase(p->type, p->id, p->psi);
 
        return simple_unlink(dir, dentry);
 }
@@ -175,8 +176,8 @@ int pstore_is_mounted(void)
  * Set the mtime & ctime to the date that this record was originally stored.
  */
 int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
-                             char *data, size_t size,
-                             struct timespec time, int (*erase)(u64))
+                 char *data, size_t size, struct timespec time,
+                 struct pstore_info *psi)
 {
        struct dentry           *root = pstore_sb->s_root;
        struct dentry           *dentry;
@@ -192,8 +193,9 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id,
        private = kmalloc(sizeof *private + size, GFP_KERNEL);
        if (!private)
                goto fail_alloc;
+       private->type = type;
        private->id = id;
-       private->erase = erase;
+       private->psi = psi;
 
        switch (type) {
        case PSTORE_TYPE_DMESG:
index 8c9f23eb16451a294cfb1b379c8cb8b55c40565d..611c1b3c46fae9b71756e1c2bd9df54e6466c17d 100644 (file)
@@ -2,5 +2,5 @@ extern void     pstore_set_kmsg_bytes(int);
 extern void    pstore_get_records(void);
 extern int     pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
                              char *data, size_t size,
-                             struct timespec time, int (*erase)(u64));
+                             struct timespec time, struct pstore_info *psi);
 extern int     pstore_is_mounted(void);
index f2c3ff20ea6857f616ddc5381cac6a90336f46b8..c5300ec316965ac456091ae91ec18f1704b97681 100644 (file)
@@ -37,6 +37,8 @@
 static DEFINE_SPINLOCK(pstore_lock);
 static struct pstore_info *psinfo;
 
+static char *backend;
+
 /* How much of the console log to snapshot */
 static unsigned long kmsg_bytes = 10240;
 
@@ -67,7 +69,8 @@ static void pstore_dump(struct kmsg_dumper *dumper,
        unsigned long   size, total = 0;
        char            *dst, *why;
        u64             id;
-       int             hsize, part = 1;
+       int             hsize;
+       unsigned int    part = 1;
 
        if (reason < ARRAY_SIZE(reason_str))
                why = reason_str[reason];
@@ -78,7 +81,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
        oopscount++;
        while (total < kmsg_bytes) {
                dst = psinfo->buf;
-               hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part++);
+               hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
                size = psinfo->bufsize - hsize;
                dst += hsize;
 
@@ -94,14 +97,16 @@ static void pstore_dump(struct kmsg_dumper *dumper,
                memcpy(dst, s1 + s1_start, l1_cpy);
                memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
 
-               id = psinfo->write(PSTORE_TYPE_DMESG, hsize + l1_cpy + l2_cpy);
+               id = psinfo->write(PSTORE_TYPE_DMESG, part,
+                                  hsize + l1_cpy + l2_cpy, psinfo);
                if (reason == KMSG_DUMP_OOPS && pstore_is_mounted())
                        pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id,
                                      psinfo->buf, hsize + l1_cpy + l2_cpy,
-                                     CURRENT_TIME, psinfo->erase);
+                                     CURRENT_TIME, psinfo);
                l1 -= l1_cpy;
                l2 -= l2_cpy;
                total += l1_cpy + l2_cpy;
+               part++;
        }
        mutex_unlock(&psinfo->buf_mutex);
 }
@@ -128,6 +133,12 @@ int pstore_register(struct pstore_info *psi)
                spin_unlock(&pstore_lock);
                return -EBUSY;
        }
+
+       if (backend && strcmp(backend, psi->name)) {
+               spin_unlock(&pstore_lock);
+               return -EINVAL;
+       }
+
        psinfo = psi;
        spin_unlock(&pstore_lock);
 
@@ -166,9 +177,9 @@ void pstore_get_records(void)
        if (rc)
                goto out;
 
-       while ((size = psi->read(&id, &type, &time)) > 0) {
+       while ((size = psi->read(&id, &type, &time, psi)) > 0) {
                if (pstore_mkfile(type, psi->name, id, psi->buf, (size_t)size,
-                                 time, psi->erase))
+                                 time, psi))
                        failed++;
        }
        psi->close(psi);
@@ -196,12 +207,15 @@ int pstore_write(enum pstore_type_id type, char *buf, size_t size)
 
        mutex_lock(&psinfo->buf_mutex);
        memcpy(psinfo->buf, buf, size);
-       id = psinfo->write(type, size);
+       id = psinfo->write(type, 0, size, psinfo);
        if (pstore_is_mounted())
                pstore_mkfile(PSTORE_TYPE_DMESG, psinfo->name, id, psinfo->buf,
-                             size, CURRENT_TIME, psinfo->erase);
+                             size, CURRENT_TIME, psinfo);
        mutex_unlock(&psinfo->buf_mutex);
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(pstore_write);
+
+module_param(backend, charp, 0444);
+MODULE_PARM_DESC(backend, "Pstore backend to use");
index 7362cf4c946a0f46b94172d7b30bac31e8602c21..6da0396e50528b2ac676c3a8d0aef72d3231ff16 100644 (file)
@@ -272,12 +272,10 @@ reiserfs_set_acl(struct reiserfs_transaction_handle *th, struct inode *inode,
        case ACL_TYPE_ACCESS:
                name = POSIX_ACL_XATTR_ACCESS;
                if (acl) {
-                       mode_t mode = inode->i_mode;
-                       error = posix_acl_equiv_mode(acl, &mode);
+                       error = posix_acl_equiv_mode(acl, &inode->i_mode);
                        if (error < 0)
                                return error;
                        else {
-                               inode->i_mode = mode;
                                if (error == 0)
                                        acl = NULL;
                        }
@@ -354,8 +352,6 @@ reiserfs_inherit_default_acl(struct reiserfs_transaction_handle *th,
                return PTR_ERR(acl);
 
        if (acl) {
-               mode_t mode = inode->i_mode;
-
                /* Copy the default ACL to the default ACL of a new directory */
                if (S_ISDIR(inode->i_mode)) {
                        err = reiserfs_set_acl(th, inode, ACL_TYPE_DEFAULT,
@@ -366,12 +362,10 @@ reiserfs_inherit_default_acl(struct reiserfs_transaction_handle *th,
 
                /* Now we reconcile the new ACL and the mode,
                   potentially modifying both */
-               err = posix_acl_create(&acl, GFP_NOFS, &mode);
+               err = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
                if (err < 0)
                        return err;
 
-               inode->i_mode = mode;
-
                /* If we need an ACL.. */
                if (err > 0)
                        err = reiserfs_set_acl(th, inode, ACL_TYPE_ACCESS, acl);
index 4a6f7f440658d4461a1f441a78516efe13a2c855..b4f2ab48a61f0b7c31cdfe3201d1f4bdded5ca50 100644 (file)
@@ -29,10 +29,7 @@ void fsstack_copy_inode_size(struct inode *dst, struct inode *src)
         *
         * We don't actually know what locking is used at the lower level;
         * but if it's a filesystem that supports quotas, it will be using
-        * i_lock as in inode_add_bytes().  tmpfs uses other locking, and
-        * its 32-bit is (just) able to exceed 2TB i_size with the aid of
-        * holes; but its i_blocks cannot carry into the upper long without
-        * almost 2TB swap - let's ignore that case.
+        * i_lock as in inode_add_bytes().
         */
        if (sizeof(i_blocks) > sizeof(long))
                spin_lock(&src->i_lock);
index 44ce516568045386e282eb03456835f87417a45a..b6c4b3795c4a000ce27ac3799b5c39ae6c8044e9 100644 (file)
@@ -221,7 +221,7 @@ xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl)
 }
 
 static int
-xfs_set_mode(struct inode *inode, mode_t mode)
+xfs_set_mode(struct inode *inode, umode_t mode)
 {
        int error = 0;
 
@@ -267,7 +267,7 @@ posix_acl_default_exists(struct inode *inode)
 int
 xfs_inherit_acl(struct inode *inode, struct posix_acl *acl)
 {
-       mode_t mode = inode->i_mode;
+       umode_t mode = inode->i_mode;
        int error = 0, inherit = 0;
 
        if (S_ISDIR(inode->i_mode)) {
@@ -381,7 +381,7 @@ xfs_xattr_acl_set(struct dentry *dentry, const char *name,
                goto out_release;
 
        if (type == ACL_TYPE_ACCESS) {
-               mode_t mode = inode->i_mode;
+               umode_t mode = inode->i_mode;
                error = posix_acl_equiv_mode(acl, &mode);
 
                if (error <= 0) {
index 3090471b2a5ef6069927b82c9bbea9bec8b2590e..e49c36d38d7ee23900f60c20ef826ea969f0fd21 100644 (file)
@@ -128,7 +128,7 @@ extern int is_dock_device(acpi_handle handle);
 extern int register_dock_notifier(struct notifier_block *nb);
 extern void unregister_dock_notifier(struct notifier_block *nb);
 extern int register_hotplug_dock_device(acpi_handle handle,
-                                       struct acpi_dock_ops *ops,
+                                       const struct acpi_dock_ops *ops,
                                        void *context);
 extern void unregister_hotplug_dock_device(acpi_handle handle);
 #else
index 2ed0a8486c1962f979e1f1d7e18b167efee0eb38..f554a9313b43d7b2bbc50d599cb5f9363bfe8868 100644 (file)
@@ -47,7 +47,7 @@
 
 /* Current ACPICA subsystem version in YYYYMMDD format */
 
-#define ACPI_CA_VERSION                 0x20110413
+#define ACPI_CA_VERSION                 0x20110623
 
 #include "actypes.h"
 #include "actbl.h"
@@ -69,6 +69,7 @@ extern u32 acpi_gbl_trace_flags;
 extern u32 acpi_gbl_enable_aml_debug_object;
 extern u8 acpi_gbl_copy_dsdt_locally;
 extern u8 acpi_gbl_truncate_io_addresses;
+extern u8 acpi_gbl_disable_auto_repair;
 
 extern u32 acpi_current_gpe_count;
 extern struct acpi_table_fadt acpi_gbl_FADT;
index e67b523a50e1a3bee89745d389b19e20466b80bd..51a527d24a8a8b03879dfaa3b059d59d32c8991b 100644 (file)
 
 extern int hest_disable;
 extern int erst_disable;
+#ifdef CONFIG_ACPI_APEI_GHES
+extern int ghes_disable;
+#else
+#define ghes_disable 1
+#endif
 
 #ifdef CONFIG_ACPI_APEI
 void __init acpi_hest_init(void);
index ba4928cae473bbca750affdc71a021e6134e469e..67055f180330b6a59f653833ebe51c8afd151a29 100644 (file)
@@ -337,7 +337,7 @@ extern struct cpuidle_driver acpi_idle_driver;
 
 /* in processor_thermal.c */
 int acpi_processor_get_limit_info(struct acpi_processor *pr);
-extern struct thermal_cooling_device_ops processor_cooling_ops;
+extern const struct thermal_cooling_device_ops processor_cooling_ops;
 #ifdef CONFIG_CPU_FREQ
 void acpi_thermal_cpufreq_init(void);
 void acpi_thermal_cpufreq_exit(void);
index 33d12f87f0e01735d41a800a73b7ea5ddc9b31a6..44335e57eaaac08c6d219f4d02d6f9eb91bf3c9a 100644 (file)
@@ -205,6 +205,8 @@ struct drm_display_info {
        enum subpixel_order subpixel_order;
        u32 color_formats;
 
+       u8 cea_rev;
+
        char *raw_edid; /* if any */
 };
 
@@ -802,6 +804,7 @@ extern struct drm_display_mode *drm_gtf_mode_complex(struct drm_device *dev,
 extern int drm_add_modes_noedid(struct drm_connector *connector,
                                int hdisplay, int vdisplay);
 
+extern int drm_edid_header_is_valid(const u8 *raw_edid);
 extern bool drm_edid_is_valid(struct edid *edid);
 struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
                                           int hsize, int vsize, int fresh);
index c4d6dbfa3ff403ec3579c33fd6d0d761cff382d5..28c0d114cb52dfaebd9756eec62187e3df56491f 100644 (file)
@@ -237,7 +237,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_GEM_GET_APERTURE        DRM_IOR  (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture)
 #define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_i915_get_pipe_from_crtc_id)
 #define DRM_IOCTL_I915_GEM_MADVISE     DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise)
-#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE       DRM_IOW(DRM_COMMAND_BASE + DRM_IOCTL_I915_OVERLAY_ATTRS, struct drm_intel_overlay_put_image)
+#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE       DRM_IOW(DRM_COMMAND_BASE + DRM_I915_OVERLAY_PUT_IMAGE, struct drm_intel_overlay_put_image)
 #define DRM_IOCTL_I915_OVERLAY_ATTRS   DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs)
 
 /* Allow drivers to submit batchbuffers directly to hardware, relying
index 1deb2a73c2daf861fb20130f17b4c6a3d924e152..6001b4da39ddc9ebd52e0f7691268abbe8ebb69d 100644 (file)
@@ -238,7 +238,6 @@ extern int acpi_paddr_to_node(u64 start_addr, u64 size);
 extern int pnpacpi_disabled;
 
 #define PXM_INVAL      (-1)
-#define NID_INVAL      (-1)
 
 int acpi_check_resource_conflict(const struct resource *res);
 
@@ -280,6 +279,8 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context);
 #define OSC_SB_CPUHP_OST_SUPPORT       8
 #define OSC_SB_APEI_SUPPORT            16
 
+extern bool osc_sb_apei_support_acked;
+
 /* PCI defined _OSC bits */
 /* _OSC DW1 Definition (OS Support Fields) */
 #define OSC_EXT_PCI_CONFIG_SUPPORT             1
index 3111385b8ca7c9e85157504630c3e73762cad768..e6e28f37d8ec472c9be9052039510fa4ce29a7cd 100644 (file)
@@ -172,8 +172,11 @@ struct pl08x_dma_chan {
        int phychan_hold;
        struct tasklet_struct tasklet;
        char *name;
-       struct pl08x_channel_data *cd;
-       dma_addr_t runtime_addr;
+       const struct pl08x_channel_data *cd;
+       dma_addr_t src_addr;
+       dma_addr_t dst_addr;
+       u32 src_cctl;
+       u32 dst_cctl;
        enum dma_data_direction runtime_direction;
        dma_cookie_t lc;
        struct list_head pend_list;
@@ -202,7 +205,7 @@ struct pl08x_dma_chan {
  * @mem_buses: buses which memory can be accessed from: PL08X_AHB1 | PL08X_AHB2
  */
 struct pl08x_platform_data {
-       struct pl08x_channel_data *slave_channels;
+       const struct pl08x_channel_data *slave_channels;
        unsigned int num_slave_channels;
        struct pl08x_channel_data memcpy_channel;
        int (*get_signal)(struct pl08x_dma_chan *);
index 3bac44cce142c94f2a5d40ab5699a07bd7aff59f..7ad634501e48cd7d39b1e8b621f0affce26765d7 100644 (file)
@@ -146,6 +146,7 @@ extern int bitmap_allocate_region(unsigned long *bitmap, int pos, int order);
 extern void bitmap_copy_le(void *dst, const unsigned long *src, int nbits);
 extern int bitmap_ord_to_pos(const unsigned long *bitmap, int n, int bits);
 
+#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG))
 #define BITMAP_LAST_WORD_MASK(nbits)                                   \
 (                                                                      \
        ((nbits) % BITS_PER_LONG) ?                                     \
index 36719ead50e8bcd0018f8e620e45533132931683..b51629e15cfcfee56982da848cfc8b47c77be18c 100644 (file)
@@ -122,6 +122,8 @@ struct cpuidle_driver {
 };
 
 #ifdef CONFIG_CPU_IDLE
+extern void disable_cpuidle(void);
+extern int cpuidle_idle_call(void);
 
 extern int cpuidle_register_driver(struct cpuidle_driver *drv);
 struct cpuidle_driver *cpuidle_get_driver(void);
@@ -135,6 +137,8 @@ extern int cpuidle_enable_device(struct cpuidle_device *dev);
 extern void cpuidle_disable_device(struct cpuidle_device *dev);
 
 #else
+static inline void disable_cpuidle(void) { }
+static inline int cpuidle_idle_call(void) { return -ENODEV; }
 
 static inline int cpuidle_register_driver(struct cpuidle_driver *drv)
 {return -ENODEV; }
index ec78a4bbe1d5b6ebbb2f4e1ab10406d700c7efe5..f9452185b0190eec50f550df8119a4c16a05bc54 100644 (file)
@@ -3,7 +3,7 @@
 
 #define SHA_DIGEST_WORDS 5
 #define SHA_MESSAGE_BYTES (512 /*bits*/ / 8)
-#define SHA_WORKSPACE_WORDS 80
+#define SHA_WORKSPACE_WORDS 16
 
 void sha_init(__u32 *buf);
 void sha_transform(__u32 *digest, const char *data, __u32 *W);
index 4427e04540516ef006e08ce8de95bebc23516905..3fa1f3d90ce0e21cb3ba27fa98d3201252f91f8e 100644 (file)
@@ -208,6 +208,49 @@ struct dm_target_callbacks {
 int dm_register_target(struct target_type *t);
 void dm_unregister_target(struct target_type *t);
 
+/*
+ * Target argument parsing.
+ */
+struct dm_arg_set {
+       unsigned argc;
+       char **argv;
+};
+
+/*
+ * The minimum and maximum value of a numeric argument, together with
+ * the error message to use if the number is found to be outside that range.
+ */
+struct dm_arg {
+       unsigned min;
+       unsigned max;
+       char *error;
+};
+
+/*
+ * Validate the next argument, either returning it as *value or, if invalid,
+ * returning -EINVAL and setting *error.
+ */
+int dm_read_arg(struct dm_arg *arg, struct dm_arg_set *arg_set,
+               unsigned *value, char **error);
+
+/*
+ * Process the next argument as the start of a group containing between
+ * arg->min and arg->max further arguments. Either return the size as
+ * *num_args or, if invalid, return -EINVAL and set *error.
+ */
+int dm_read_arg_group(struct dm_arg *arg, struct dm_arg_set *arg_set,
+                     unsigned *num_args, char **error);
+
+/*
+ * Return the current argument and shift to the next.
+ */
+const char *dm_shift_arg(struct dm_arg_set *as);
+
+/*
+ * Move through num_args arguments.
+ */
+void dm_consume_args(struct dm_arg_set *as, unsigned num_args);
+
 /*-----------------------------------------------------------------
  * Functions for creating and manipulating mapped devices.
  * Drop the reference with dm_put when you finish with the object.
index 3708455ee6c38f49cb8d7fd97b040f100a3379aa..0cb8eff76bd6e563999df6c081251c4f8a8ef292 100644 (file)
@@ -267,9 +267,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       20
+#define DM_VERSION_MINOR       21
 #define DM_VERSION_PATCHLEVEL  0
-#define DM_VERSION_EXTRA       "-ioctl (2011-02-02)"
+#define DM_VERSION_EXTRA       "-ioctl (2011-07-06)"
 
 /* Status bits */
 #define DM_READONLY_FLAG       (1 << 0) /* In/Out */
index 298d587e349b17169ad1a295645f66e307f42ff4..5e54458e920f36466a29d83ccbe913a03f347a01 100644 (file)
@@ -42,5 +42,20 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
                   unsigned num_dests, struct dm_io_region *dests,
                   unsigned flags, dm_kcopyd_notify_fn fn, void *context);
 
+/*
+ * Prepare a callback and submit it via the kcopyd thread.
+ *
+ * dm_kcopyd_prepare_callback allocates a callback structure and returns it.
+ * It must not be called from interrupt context.
+ * The returned value should be passed into dm_kcopyd_do_callback.
+ *
+ * dm_kcopyd_do_callback submits the callback.
+ * It may be called from interrupt context.
+ * The callback is issued from the kcopyd thread.
+ */
+void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc,
+                                dm_kcopyd_notify_fn fn, void *context);
+void dm_kcopyd_do_callback(void *job, int read_err, unsigned long write_err);
+
 #endif /* __KERNEL__ */
 #endif /* _LINUX_DM_KCOPYD_H */
index ec257269392590b07729b1c99f8b3e835dba47e5..2362a0bc7f0dbfe0c4cb7df2e31ef769ebcbdacc 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/rtc.h>
 #include <linux/ioport.h>
 #include <linux/pfn.h>
+#include <linux/pstore.h>
 
 #include <asm/page.h>
 #include <asm/system.h>
@@ -232,6 +233,9 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules,
 #define UV_SYSTEM_TABLE_GUID \
     EFI_GUID(  0x3b13a7d4, 0x633e, 0x11dd, 0x93, 0xec, 0xda, 0x25, 0x56, 0xd8, 0x95, 0x93 )
 
+#define LINUX_EFI_CRASH_GUID \
+    EFI_GUID(  0xcfc8fc79, 0xbe2e, 0x4ddc, 0x97, 0xf0, 0x9f, 0x98, 0xbf, 0xe2, 0x98, 0xa0 )
+
 typedef struct {
        efi_guid_t guid;
        unsigned long table;
@@ -458,6 +462,8 @@ struct efivars {
        struct kset *kset;
        struct bin_attribute *new_var, *del_var;
        const struct efivar_operations *ops;
+       struct efivar_entry *walk_entry;
+       struct pstore_info efi_pstore_info;
 };
 
 int register_efivars(struct efivars *efivars,
index 3ff060ac7810586d93685942195dc386fdcc0f69..c6f996f2abb6c552345884760bfc5d5967d40bdb 100644 (file)
@@ -25,10 +25,6 @@ struct fault_attr {
        unsigned long reject_end;
 
        unsigned long count;
-
-#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
-       struct dentry *dir;
-#endif
 };
 
 #define FAULT_ATTR_INITIALIZER {                               \
@@ -45,19 +41,15 @@ bool should_fail(struct fault_attr *attr, ssize_t size);
 
 #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
 
-int init_fault_attr_dentries(struct fault_attr *attr, const char *name);
-void cleanup_fault_attr_dentries(struct fault_attr *attr);
+struct dentry *fault_create_debugfs_attr(const char *name,
+                       struct dentry *parent, struct fault_attr *attr);
 
 #else /* CONFIG_FAULT_INJECTION_DEBUG_FS */
 
-static inline int init_fault_attr_dentries(struct fault_attr *attr,
-                                         const char *name)
-{
-       return -ENODEV;
-}
-
-static inline void cleanup_fault_attr_dentries(struct fault_attr *attr)
+static inline struct dentry *fault_create_debugfs_attr(const char *name,
+                       struct dentry *parent, struct fault_attr *attr)
 {
+       return ERR_PTR(-ENODEV);
 }
 
 #endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
index f23bcb77260c236f74441d02f3e00cd1d5c62c3a..786b3b1113cfb1acd5bf59848d04fe2e460e2de0 100644 (file)
@@ -2317,11 +2317,18 @@ extern int should_remove_suid(struct dentry *);
 extern int file_remove_suid(struct file *);
 
 extern void __insert_inode_hash(struct inode *, unsigned long hashval);
-extern void remove_inode_hash(struct inode *);
 static inline void insert_inode_hash(struct inode *inode)
 {
        __insert_inode_hash(inode, inode->i_ino);
 }
+
+extern void __remove_inode_hash(struct inode *);
+static inline void remove_inode_hash(struct inode *inode)
+{
+       if (!inode_unhashed(inode))
+               __remove_inode_hash(inode);
+}
+
 extern void inode_sb_list_add(struct inode *inode);
 
 #ifdef CONFIG_BLOCK
index 5bbebda78b025d8bc63b2c24e960d20733fa2613..5e98eeb2af3b970220c7743016f9f85b0213ae8b 100644 (file)
@@ -1,8 +1,26 @@
 /*
- * Basic general purpose allocator for managing special purpose memory
- * not managed by the regular kmalloc/kfree interface.
- * Uses for this includes on-device special memory, uncached memory
- * etc.
+ * Basic general purpose allocator for managing special purpose
+ * memory, for example, memory that is not managed by the regular
+ * kmalloc/kfree interface.  Uses for this includes on-device special
+ * memory, uncached memory etc.
+ *
+ * It is safe to use the allocator in NMI handlers and other special
+ * unblockable contexts that could otherwise deadlock on locks.  This
+ * is implemented by using atomic operations and retries on any
+ * conflicts.  The disadvantage is that there may be livelocks in
+ * extreme cases.  For better scalability, one allocator can be used
+ * for each CPU.
+ *
+ * The lockless operation only works if there is enough memory
+ * available.  If new memory is added to the pool a lock has to be
+ * still taken.  So any user relying on locklessness has to ensure
+ * that sufficient memory is preallocated.
+ *
+ * The basic atomic operation of this allocator is cmpxchg on long.
+ * On architectures that don't have NMI-safe cmpxchg implementation,
+ * the allocator can NOT be used in NMI handler.  So code uses the
+ * allocator in NMI handler should depend on
+ * CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG.
  *
  * This source code is licensed under the GNU General Public License,
  * Version 2.  See the file COPYING for more details.
@@ -15,7 +33,7 @@
  *  General purpose special memory pool descriptor.
  */
 struct gen_pool {
-       rwlock_t lock;
+       spinlock_t lock;
        struct list_head chunks;        /* list of chunks in this pool */
        int min_alloc_order;            /* minimum allocation order */
 };
@@ -24,8 +42,8 @@ struct gen_pool {
  *  General purpose special memory pool chunk descriptor.
  */
 struct gen_pool_chunk {
-       spinlock_t lock;
        struct list_head next_chunk;    /* next chunk in pool */
+       atomic_t avail;
        phys_addr_t phys_addr;          /* physical starting address of memory chunk */
        unsigned long start_addr;       /* starting address of memory chunk */
        unsigned long end_addr;         /* ending address of memory chunk */
@@ -56,4 +74,8 @@ static inline int gen_pool_add(struct gen_pool *pool, unsigned long addr,
 extern void gen_pool_destroy(struct gen_pool *);
 extern unsigned long gen_pool_alloc(struct gen_pool *, size_t);
 extern void gen_pool_free(struct gen_pool *, unsigned long, size_t);
+extern void gen_pool_for_each_chunk(struct gen_pool *,
+       void (*)(struct gen_pool *, struct gen_pool_chunk *, void *), void *);
+extern size_t gen_pool_avail(struct gen_pool *);
+extern size_t gen_pool_size(struct gen_pool *);
 #endif /* __GENALLOC_H__ */
index cb4089254f01feb22fa7db68841f81e7e6b2569c..3a76faf6a3ee82cd20f82d36208c6f50c8f29685 100644 (file)
@@ -92,7 +92,7 @@ struct vm_area_struct;
  */
 #define __GFP_NOTRACK_FALSE_POSITIVE (__GFP_NOTRACK)
 
-#define __GFP_BITS_SHIFT 23    /* Room for 23 __GFP_FOO bits */
+#define __GFP_BITS_SHIFT 24    /* Room for N __GFP_FOO bits */
 #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
 
 /* This equals 0, but use constants in case they ever change */
index 13a801f3d028225533f2ce701349b2b0657ca734..255491cf522e1d08ea6d0066fc7872f32817c82c 100644 (file)
@@ -146,6 +146,10 @@ void ida_remove(struct ida *ida, int id);
 void ida_destroy(struct ida *ida);
 void ida_init(struct ida *ida);
 
+int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
+                  gfp_t gfp_mask);
+void ida_simple_remove(struct ida *ida, unsigned int id);
+
 void __init idr_init_cache(void);
 
 #endif /* __IDR_H__ */
index 068784e17972a1ffd907f4365bd5f774e70bcfdf..a637e781433499c8283f2991989ff35a35b7e66c 100644 (file)
@@ -438,6 +438,8 @@ struct input_keymap_entry {
 #define KEY_WIMAX              246
 #define KEY_RFKILL             247     /* Key that controls all radios */
 
+#define KEY_MICMUTE            248     /* Mute / unmute the microphone */
+
 /* Code 255 is reserved for special needs of AT keyboard driver */
 
 #define BTN_MISC               0x100
index d087c2e7b2aa0303a22b181cdc617a67e99cff80..38f307b8c3342c4bef86ac3366c785696df54cce 100644 (file)
@@ -1329,12 +1329,6 @@ extern int jbd_blocks_per_page(struct inode *inode);
 #define BUFFER_TRACE2(bh, bh2, info)   do {} while (0)
 #define JBUFFER_TRACE(jh, info)        do {} while (0)
 
-/* 
- * jbd2_dev_to_name is a utility function used by the jbd2 and ext4 
- * tracing infrastructure to map a dev_t to a device name.
- */
-extern const char *jbd2_dev_to_name(dev_t device);
-
 #endif /* __KERNEL__ */
 
 #endif /* _LINUX_JBD2_H */
diff --git a/include/linux/llist.h b/include/linux/llist.h
new file mode 100644 (file)
index 0000000..aa0c8b5
--- /dev/null
@@ -0,0 +1,126 @@
+#ifndef LLIST_H
+#define LLIST_H
+/*
+ * Lock-less NULL terminated single linked list
+ *
+ * If there are multiple producers and multiple consumers, llist_add
+ * can be used in producers and llist_del_all can be used in
+ * consumers.  They can work simultaneously without lock.  But
+ * llist_del_first can not be used here.  Because llist_del_first
+ * depends on list->first->next does not changed if list->first is not
+ * changed during its operation, but llist_del_first, llist_add,
+ * llist_add (or llist_del_all, llist_add, llist_add) sequence in
+ * another consumer may violate that.
+ *
+ * If there are multiple producers and one consumer, llist_add can be
+ * used in producers and llist_del_all or llist_del_first can be used
+ * in the consumer.
+ *
+ * This can be summarized as follow:
+ *
+ *           |   add    | del_first |  del_all
+ * add       |    -     |     -     |     -
+ * del_first |          |     L     |     L
+ * del_all   |          |           |     -
+ *
+ * Where "-" stands for no lock is needed, while "L" stands for lock
+ * is needed.
+ *
+ * The list entries deleted via llist_del_all can be traversed with
+ * traversing function such as llist_for_each etc.  But the list
+ * entries can not be traversed safely before deleted from the list.
+ * The order of deleted entries is from the newest to the oldest added
+ * one.  If you want to traverse from the oldest to the newest, you
+ * must reverse the order by yourself before traversing.
+ *
+ * The basic atomic operation of this list is cmpxchg on long.  On
+ * architectures that don't have NMI-safe cmpxchg implementation, the
+ * list can NOT be used in NMI handler.  So code uses the list in NMI
+ * handler should depend on CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG.
+ */
+
+struct llist_head {
+       struct llist_node *first;
+};
+
+struct llist_node {
+       struct llist_node *next;
+};
+
+#define LLIST_HEAD_INIT(name)  { NULL }
+#define LLIST_HEAD(name)       struct llist_head name = LLIST_HEAD_INIT(name)
+
+/**
+ * init_llist_head - initialize lock-less list head
+ * @head:      the head for your lock-less list
+ */
+static inline void init_llist_head(struct llist_head *list)
+{
+       list->first = NULL;
+}
+
+/**
+ * llist_entry - get the struct of this entry
+ * @ptr:       the &struct llist_node pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the llist_node within the struct.
+ */
+#define llist_entry(ptr, type, member)         \
+       container_of(ptr, type, member)
+
+/**
+ * llist_for_each - iterate over some deleted entries of a lock-less list
+ * @pos:       the &struct llist_node to use as a loop cursor
+ * @node:      the first entry of deleted list entries
+ *
+ * In general, some entries of the lock-less list can be traversed
+ * safely only after being deleted from list, so start with an entry
+ * instead of list head.
+ *
+ * If being used on entries deleted from lock-less list directly, the
+ * traverse order is from the newest to the oldest added entry.  If
+ * you want to traverse from the oldest to the newest, you must
+ * reverse the order by yourself before traversing.
+ */
+#define llist_for_each(pos, node)                      \
+       for ((pos) = (node); pos; (pos) = (pos)->next)
+
+/**
+ * llist_for_each_entry - iterate over some deleted entries of lock-less list of given type
+ * @pos:       the type * to use as a loop cursor.
+ * @node:      the fist entry of deleted list entries.
+ * @member:    the name of the llist_node with the struct.
+ *
+ * In general, some entries of the lock-less list can be traversed
+ * safely only after being removed from list, so start with an entry
+ * instead of list head.
+ *
+ * If being used on entries deleted from lock-less list directly, the
+ * traverse order is from the newest to the oldest added entry.  If
+ * you want to traverse from the oldest to the newest, you must
+ * reverse the order by yourself before traversing.
+ */
+#define llist_for_each_entry(pos, node, member)                                \
+       for ((pos) = llist_entry((node), typeof(*(pos)), member);       \
+            &(pos)->member != NULL;                                    \
+            (pos) = llist_entry((pos)->member.next, typeof(*(pos)), member))
+
+/**
+ * llist_empty - tests whether a lock-less list is empty
+ * @head:      the list to test
+ *
+ * Not guaranteed to be accurate or up to date.  Just a quick way to
+ * test whether the list is empty without deleting something from the
+ * list.
+ */
+static inline int llist_empty(const struct llist_head *head)
+{
+       return ACCESS_ONCE(head->first) == NULL;
+}
+
+void llist_add(struct llist_node *new, struct llist_head *head);
+void llist_add_batch(struct llist_node *new_first, struct llist_node *new_last,
+                    struct llist_head *head);
+struct llist_node *llist_del_first(struct llist_head *head);
+struct llist_node *llist_del_all(struct llist_head *head);
+#endif /* LLIST_H */
index b96600786913a3ede3e03a698979a240262273cf..3b535db00a94983324d42106fae5bd05670f3157 100644 (file)
@@ -86,8 +86,6 @@ extern void mem_cgroup_uncharge_end(void);
 
 extern void mem_cgroup_uncharge_page(struct page *page);
 extern void mem_cgroup_uncharge_cache_page(struct page *page);
-extern int mem_cgroup_shmem_charge_fallback(struct page *page,
-                       struct mm_struct *mm, gfp_t gfp_mask);
 
 extern void mem_cgroup_out_of_memory(struct mem_cgroup *mem, gfp_t gfp_mask);
 int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem);
@@ -225,12 +223,6 @@ static inline void mem_cgroup_uncharge_cache_page(struct page *page)
 {
 }
 
-static inline int mem_cgroup_shmem_charge_fallback(struct page *page,
-                       struct mm_struct *mm, gfp_t gfp_mask)
-{
-       return 0;
-}
-
 static inline void mem_cgroup_add_lru_list(struct page *page, int lru)
 {
 }
index 89212df05622193a801452195953e770a18c90a9..f7316c29bdec6a06f1e57890ed42efef5be7a443 100644 (file)
@@ -89,7 +89,7 @@ enum aat2870_id {
 
 /* Backlight current magnitude (mA) */
 enum aat2870_current {
-       AAT2870_CURRENT_0_45,
+       AAT2870_CURRENT_0_45 = 1,
        AAT2870_CURRENT_0_90,
        AAT2870_CURRENT_1_80,
        AAT2870_CURRENT_2_70,
index 3172a1c0f08e96faf072fda7b4327a9626ddf611..f2690cf49827abc641d2bf59d8dc359b7bbce15e 100644 (file)
@@ -1600,6 +1600,7 @@ enum mf_flags {
 };
 extern void memory_failure(unsigned long pfn, int trapno);
 extern int __memory_failure(unsigned long pfn, int trapno, int flags);
+extern void memory_failure_queue(unsigned long pfn, int trapno, int flags);
 extern int unpoison_memory(unsigned long pfn);
 extern int sysctl_memory_failure_early_kill;
 extern int sysctl_memory_failure_recovery;
index b96fb99072ffdb4fbdb687438d19d5a629106a02..eaac770f886ea51824f694975fff86887ad035bb 100644 (file)
@@ -569,12 +569,12 @@ extern struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type);
 extern int nfs3_proc_setacl(struct inode *inode, int type,
                            struct posix_acl *acl);
 extern int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode,
-               mode_t mode);
+               umode_t mode);
 extern void nfs3_forget_cached_acls(struct inode *inode);
 #else
 static inline int nfs3_proc_set_default_acl(struct inode *dir,
                                            struct inode *inode,
-                                           mode_t mode)
+                                           umode_t mode)
 {
        return 0;
 }
index bd716f8908de022b1b391401c5c877aed9a85ba8..0085bb01c041e969276b2ab5107dc4e2446d445c 100644 (file)
@@ -196,12 +196,13 @@ extern struct property *of_find_property(const struct device_node *np,
                                         const char *name,
                                         int *lenp);
 extern int of_property_read_u32_array(const struct device_node *np,
-                                     char *propname,
+                                     const char *propname,
                                      u32 *out_values,
                                      size_t sz);
 
-extern int of_property_read_string(struct device_node *np, char *propname,
-                                       const char **out_string);
+extern int of_property_read_string(struct device_node *np,
+                                  const char *propname,
+                                  const char **out_string);
 extern int of_device_is_compatible(const struct device_node *device,
                                   const char *);
 extern int of_device_is_available(const struct device_node *device);
@@ -242,13 +243,15 @@ static inline bool of_have_populated_dt(void)
 }
 
 static inline int of_property_read_u32_array(const struct device_node *np,
-                               char *propname, u32 *out_values, size_t sz)
+                                            const char *propname,
+                                            u32 *out_values, size_t sz)
 {
        return -ENOSYS;
 }
 
 static inline int of_property_read_string(struct device_node *np,
-                               char *propname, const char **out_string)
+                                         const char *propname,
+                                         const char **out_string)
 {
        return -ENOSYS;
 }
@@ -256,7 +259,7 @@ static inline int of_property_read_string(struct device_node *np,
 #endif /* CONFIG_OF */
 
 static inline int of_property_read_u32(const struct device_node *np,
-                                      char *propname,
+                                      const char *propname,
                                       u32 *out_value)
 {
        return of_property_read_u32_array(np, propname, out_value, 1);
index b00c4ec5056e2f870830d2a68380e86385df679a..ae96bbe54518449fc66357b5405d0a175e61ca28 100644 (file)
 #define PCI_DEVICE_ID_INTEL_ICH10_5    0x3a60
 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MIN      0x3b00
 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_LPC_MAX      0x3b1f
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB0  0x3c20
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB1  0x3c21
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB2  0x3c22
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB3  0x3c23
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB4  0x3c24
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB5  0x3c25
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB6  0x3c26
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB7  0x3c27
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB8  0x3c2e
+#define PCI_DEVICE_ID_INTEL_IOAT_SNB9  0x3c2f
 #define PCI_DEVICE_ID_INTEL_IOAT_SNB   0x402f
 #define PCI_DEVICE_ID_INTEL_5100_16    0x65f0
 #define PCI_DEVICE_ID_INTEL_5100_21    0x65f5
index 9a53b99818e28d2302cd004eaf7ef0f05005d1af..b7681102a4b9a306409d261f04db33b482378e4c 100644 (file)
@@ -9,6 +9,7 @@
 #define __LINUX_POSIX_ACL_H
 
 #include <linux/slab.h>
+#include <linux/rcupdate.h>
 
 #define ACL_UNDEFINED_ID       (-1)
 
@@ -38,7 +39,10 @@ struct posix_acl_entry {
 };
 
 struct posix_acl {
-       atomic_t                a_refcount;
+       union {
+               atomic_t                a_refcount;
+               struct rcu_head         a_rcu;
+       };
        unsigned int            a_count;
        struct posix_acl_entry  a_entries[0];
 };
@@ -65,7 +69,7 @@ static inline void
 posix_acl_release(struct posix_acl *acl)
 {
        if (acl && atomic_dec_and_test(&acl->a_refcount))
-               kfree(acl);
+               kfree_rcu(acl, a_rcu);
 }
 
 
@@ -75,29 +79,31 @@ extern void posix_acl_init(struct posix_acl *, int);
 extern struct posix_acl *posix_acl_alloc(int, gfp_t);
 extern int posix_acl_valid(const struct posix_acl *);
 extern int posix_acl_permission(struct inode *, const struct posix_acl *, int);
-extern struct posix_acl *posix_acl_from_mode(mode_t, gfp_t);
-extern int posix_acl_equiv_mode(const struct posix_acl *, mode_t *);
-extern int posix_acl_create(struct posix_acl **, gfp_t, mode_t *);
-extern int posix_acl_chmod(struct posix_acl **, gfp_t, mode_t);
+extern struct posix_acl *posix_acl_from_mode(umode_t, gfp_t);
+extern int posix_acl_equiv_mode(const struct posix_acl *, umode_t *);
+extern int posix_acl_create(struct posix_acl **, gfp_t, umode_t *);
+extern int posix_acl_chmod(struct posix_acl **, gfp_t, umode_t);
 
 extern struct posix_acl *get_posix_acl(struct inode *, int);
 extern int set_posix_acl(struct inode *, int, struct posix_acl *);
 
 #ifdef CONFIG_FS_POSIX_ACL
-static inline struct posix_acl *get_cached_acl(struct inode *inode, int type)
+static inline struct posix_acl **acl_by_type(struct inode *inode, int type)
 {
-       struct posix_acl **p, *acl;
        switch (type) {
        case ACL_TYPE_ACCESS:
-               p = &inode->i_acl;
-               break;
+               return &inode->i_acl;
        case ACL_TYPE_DEFAULT:
-               p = &inode->i_default_acl;
-               break;
+               return &inode->i_default_acl;
        default:
-               return ERR_PTR(-EINVAL);
+               BUG();
        }
-       acl = ACCESS_ONCE(*p);
+}
+
+static inline struct posix_acl *get_cached_acl(struct inode *inode, int type)
+{
+       struct posix_acl **p = acl_by_type(inode, type);
+       struct posix_acl *acl = ACCESS_ONCE(*p);
        if (acl) {
                spin_lock(&inode->i_lock);
                acl = *p;
@@ -108,41 +114,20 @@ static inline struct posix_acl *get_cached_acl(struct inode *inode, int type)
        return acl;
 }
 
-static inline int negative_cached_acl(struct inode *inode, int type)
+static inline struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type)
 {
-       struct posix_acl **p, *acl;
-       switch (type) {
-       case ACL_TYPE_ACCESS:
-               p = &inode->i_acl;
-               break;
-       case ACL_TYPE_DEFAULT:
-               p = &inode->i_default_acl;
-               break;
-       default:
-               BUG();
-       }
-       acl = ACCESS_ONCE(*p);
-       if (acl)
-               return 0;
-       return 1;
+       return rcu_dereference(*acl_by_type(inode, type));
 }
 
 static inline void set_cached_acl(struct inode *inode,
                                  int type,
                                  struct posix_acl *acl)
 {
-       struct posix_acl *old = NULL;
+       struct posix_acl **p = acl_by_type(inode, type);
+       struct posix_acl *old;
        spin_lock(&inode->i_lock);
-       switch (type) {
-       case ACL_TYPE_ACCESS:
-               old = inode->i_acl;
-               inode->i_acl = posix_acl_dup(acl);
-               break;
-       case ACL_TYPE_DEFAULT:
-               old = inode->i_default_acl;
-               inode->i_default_acl = posix_acl_dup(acl);
-               break;
-       }
+       old = *p;
+       rcu_assign_pointer(*p, posix_acl_dup(acl));
        spin_unlock(&inode->i_lock);
        if (old != ACL_NOT_CACHED)
                posix_acl_release(old);
@@ -150,18 +135,11 @@ static inline void set_cached_acl(struct inode *inode,
 
 static inline void forget_cached_acl(struct inode *inode, int type)
 {
-       struct posix_acl *old = NULL;
+       struct posix_acl **p = acl_by_type(inode, type);
+       struct posix_acl *old;
        spin_lock(&inode->i_lock);
-       switch (type) {
-       case ACL_TYPE_ACCESS:
-               old = inode->i_acl;
-               inode->i_acl = ACL_NOT_CACHED;
-               break;
-       case ACL_TYPE_DEFAULT:
-               old = inode->i_default_acl;
-               inode->i_default_acl = ACL_NOT_CACHED;
-               break;
-       }
+       old = *p;
+       *p = ACL_NOT_CACHED;
        spin_unlock(&inode->i_lock);
        if (old != ACL_NOT_CACHED)
                posix_acl_release(old);
index 2455ef2683f050660b32bb1c81fc5a381d775e9f..cc03bbf5c4b8e4dbe8c2b91b39f7a1b83b866fcb 100644 (file)
@@ -38,9 +38,12 @@ struct pstore_info {
        int             (*open)(struct pstore_info *psi);
        int             (*close)(struct pstore_info *psi);
        ssize_t         (*read)(u64 *id, enum pstore_type_id *type,
-                       struct timespec *time);
-       u64             (*write)(enum pstore_type_id type, size_t size);
-       int             (*erase)(u64 id);
+                       struct timespec *time, struct pstore_info *psi);
+       u64             (*write)(enum pstore_type_id type, unsigned int part,
+                       size_t size, struct pstore_info *psi);
+       int             (*erase)(enum pstore_type_id type, u64 id,
+                       struct pstore_info *psi);
+       void            *data;
 };
 
 #ifdef CONFIG_PSTORE
index 23241c2feccee7b542ac1e984c91b13a02be0541..9d4539c52e53e5f3635345260049999ba90d735e 100644 (file)
  * when it is shrunk, before we rcu free the node. See shrink code for
  * details.
  */
-#define RADIX_TREE_INDIRECT_PTR        1
+#define RADIX_TREE_INDIRECT_PTR                1
+/*
+ * A common use of the radix tree is to store pointers to struct pages;
+ * but shmem/tmpfs needs also to store swap entries in the same tree:
+ * those are marked as exceptional entries to distinguish them.
+ * EXCEPTIONAL_ENTRY tests the bit, EXCEPTIONAL_SHIFT shifts content past it.
+ */
+#define RADIX_TREE_EXCEPTIONAL_ENTRY   2
+#define RADIX_TREE_EXCEPTIONAL_SHIFT   2
 
 #define radix_tree_indirect_to_ptr(ptr) \
        radix_tree_indirect_to_ptr((void __force *)(ptr))
@@ -173,6 +181,28 @@ static inline int radix_tree_deref_retry(void *arg)
        return unlikely((unsigned long)arg & RADIX_TREE_INDIRECT_PTR);
 }
 
+/**
+ * radix_tree_exceptional_entry        - radix_tree_deref_slot gave exceptional entry?
+ * @arg:       value returned by radix_tree_deref_slot
+ * Returns:    0 if well-aligned pointer, non-0 if exceptional entry.
+ */
+static inline int radix_tree_exceptional_entry(void *arg)
+{
+       /* Not unlikely because radix_tree_exception often tested first */
+       return (unsigned long)arg & RADIX_TREE_EXCEPTIONAL_ENTRY;
+}
+
+/**
+ * radix_tree_exception        - radix_tree_deref_slot returned either exception?
+ * @arg:       value returned by radix_tree_deref_slot
+ * Returns:    0 if well-aligned pointer, non-0 if either kind of exception.
+ */
+static inline int radix_tree_exception(void *arg)
+{
+       return unlikely((unsigned long)arg &
+               (RADIX_TREE_INDIRECT_PTR | RADIX_TREE_EXCEPTIONAL_ENTRY));
+}
+
 /**
  * radix_tree_replace_slot     - replace item in a slot
  * @pslot:     pointer to slot, returned by radix_tree_lookup_slot
@@ -194,8 +224,8 @@ void *radix_tree_delete(struct radix_tree_root *, unsigned long);
 unsigned int
 radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
                        unsigned long first_index, unsigned int max_items);
-unsigned int
-radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results,
+unsigned int radix_tree_gang_lookup_slot(struct radix_tree_root *root,
+                       void ***results, unsigned long *indices,
                        unsigned long first_index, unsigned int max_items);
 unsigned long radix_tree_next_hole(struct radix_tree_root *root,
                                unsigned long index, unsigned long max_scan);
@@ -222,6 +252,7 @@ unsigned long radix_tree_range_tag_if_tagged(struct radix_tree_root *root,
                unsigned long nr_to_tag,
                unsigned int fromtag, unsigned int totag);
 int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag);
+unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item);
 
 static inline void radix_tree_preload_end(void)
 {
index 9e87c1cb7270ff458833471b55ead7e3a440c1a0..26f6ea4444e39fedda935b5505439a6de3301f41 100644 (file)
@@ -122,6 +122,9 @@ struct regulator;
 struct regulator_bulk_data {
        const char *supply;
        struct regulator *consumer;
+
+       /* Internal use */
+       int ret;
 };
 
 #if defined(CONFIG_REGULATOR)
index 6c433b89c80d617da2902caeffa74280fdaa9efd..1a80bc77517d03870ea9f34a6d2c317953445bc4 100644 (file)
@@ -188,18 +188,16 @@ struct regulator_dev {
 
        /* lists we belong to */
        struct list_head list; /* list of all regulators */
-       struct list_head slist; /* list of supplied regulators */
 
        /* lists we own */
        struct list_head consumer_list; /* consumers we supply */
-       struct list_head supply_list; /* regulators we supply */
 
        struct blocking_notifier_head notifier;
        struct mutex mutex; /* consumer lock */
        struct module *owner;
        struct device dev;
        struct regulation_constraints *constraints;
-       struct regulator_dev *supply;   /* for tree */
+       struct regulator *supply;       /* for tree */
 
        void *reg_data;         /* regulator_dev data */
 
index aa08fa8fd79b9c85bd3adaa68e22631b0f425e97..9291ac3cc6276829900546ec0703809893d33f77 100644 (file)
@@ -8,22 +8,15 @@
 
 /* inode in-kernel data */
 
-#define SHMEM_NR_DIRECT 16
-
-#define SHMEM_SYMLINK_INLINE_LEN (SHMEM_NR_DIRECT * sizeof(swp_entry_t))
-
 struct shmem_inode_info {
        spinlock_t              lock;
        unsigned long           flags;
        unsigned long           alloced;        /* data pages alloced to file */
-       unsigned long           swapped;        /* subtotal assigned to swap */
-       unsigned long           next_index;     /* highest alloced index + 1 */
-       struct shared_policy    policy;         /* NUMA memory alloc policy */
-       struct page             *i_indirect;    /* top indirect blocks page */
        union {
-               swp_entry_t     i_direct[SHMEM_NR_DIRECT]; /* first blocks */
-               char            inline_symlink[SHMEM_SYMLINK_INLINE_LEN];
+               unsigned long   swapped;        /* subtotal assigned to swap */
+               char            *symlink;       /* unswappable short symlink */
        };
+       struct shared_policy    policy;         /* NUMA memory alloc policy */
        struct list_head        swaplist;       /* chain of maybes on swap */
        struct list_head        xattr_list;     /* list of shmem_xattr */
        struct inode            vfs_inode;
@@ -49,7 +42,7 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
 /*
  * Functions in mm/shmem.c called directly from elsewhere:
  */
-extern int init_tmpfs(void);
+extern int shmem_init(void);
 extern int shmem_fill_super(struct super_block *sb, void *data, int silent);
 extern struct file *shmem_file_setup(const char *name,
                                        loff_t size, unsigned long flags);
@@ -59,8 +52,6 @@ extern struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
                                        pgoff_t index, gfp_t gfp_mask);
 extern void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end);
 extern int shmem_unuse(swp_entry_t entry, struct page *page);
-extern void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t pgoff,
-                                       struct page **pagep, swp_entry_t *ent);
 
 static inline struct page *shmem_read_mapping_page(
                                struct address_space *mapping, pgoff_t index)
index cd42e30b7c6eb5e1b04bd69ca393f1c7878b3351..2189d3ffc85dc1adf60a775023077e8b9f395ada 100644 (file)
@@ -1,3 +1,8 @@
+#ifndef _LINUX_SWAPOPS_H
+#define _LINUX_SWAPOPS_H
+
+#include <linux/radix-tree.h>
+
 /*
  * swapcache pages are stored in the swapper_space radix tree.  We want to
  * get good packing density in that tree, so the index should be dense in
@@ -76,6 +81,22 @@ static inline pte_t swp_entry_to_pte(swp_entry_t entry)
        return __swp_entry_to_pte(arch_entry);
 }
 
+static inline swp_entry_t radix_to_swp_entry(void *arg)
+{
+       swp_entry_t entry;
+
+       entry.val = (unsigned long)arg >> RADIX_TREE_EXCEPTIONAL_SHIFT;
+       return entry;
+}
+
+static inline void *swp_to_radix_entry(swp_entry_t entry)
+{
+       unsigned long value;
+
+       value = entry.val << RADIX_TREE_EXCEPTIONAL_SHIFT;
+       return (void *)(value | RADIX_TREE_EXCEPTIONAL_ENTRY);
+}
+
 #ifdef CONFIG_MIGRATION
 static inline swp_entry_t make_migration_entry(struct page *page, int write)
 {
@@ -169,3 +190,5 @@ static inline int non_swap_entry(swp_entry_t entry)
        return 0;
 }
 #endif
+
+#endif /* _LINUX_SWAPOPS_H */
index d3ec89fb4122610c26dfe0d86f499d50539280ff..47b4a27e6e97c27f1d2bd6cfab895f4e36adb37c 100644 (file)
@@ -85,22 +85,6 @@ struct thermal_cooling_device {
                                ((long)t-2732+5)/10 : ((long)t-2732-5)/10)
 #define CELSIUS_TO_KELVIN(t)   ((t)*10+2732)
 
-#if defined(CONFIG_THERMAL_HWMON)
-/* thermal zone devices with the same type share one hwmon device */
-struct thermal_hwmon_device {
-       char type[THERMAL_NAME_LENGTH];
-       struct device *device;
-       int count;
-       struct list_head tz_list;
-       struct list_head node;
-};
-
-struct thermal_hwmon_attr {
-       struct device_attribute attr;
-       char name[16];
-};
-#endif
-
 struct thermal_zone_device {
        int id;
        char type[THERMAL_NAME_LENGTH];
@@ -120,12 +104,6 @@ struct thermal_zone_device {
        struct mutex lock;      /* protect cooling devices list */
        struct list_head node;
        struct delayed_work poll_queue;
-#if defined(CONFIG_THERMAL_HWMON)
-       struct list_head hwmon_node;
-       struct thermal_hwmon_device *hwmon;
-       struct thermal_hwmon_attr temp_input;   /* hwmon sys attr */
-       struct thermal_hwmon_attr temp_crit;    /* hwmon sys attr */
-#endif
 };
 /* Adding event notification support elements */
 #define THERMAL_GENL_FAMILY_NAME                "thermal_event"
index 3b938743514bd571f0404e0f1369fc05591c9680..9808877c2ab91a609a79494fbd4945dd8def5bd3 100644 (file)
@@ -8,7 +8,7 @@
  * have chosen to adopt the protocol and over the years it has become a
  * de-facto standard for labeled networking.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index 29e255796ce1fdb0875f4da4a652054b48fb72ce..13d507d69ddbcfb01bd717ba1fd2659cc74e53d6 100644 (file)
@@ -37,7 +37,7 @@ struct dst_entry {
        unsigned long           _metrics;
        unsigned long           expires;
        struct dst_entry        *path;
-       struct neighbour        *_neighbour;
+       struct neighbour __rcu  *_neighbour;
 #ifdef CONFIG_XFRM
        struct xfrm_state       *xfrm;
 #else
@@ -88,12 +88,17 @@ struct dst_entry {
 
 static inline struct neighbour *dst_get_neighbour(struct dst_entry *dst)
 {
-       return dst->_neighbour;
+       return rcu_dereference(dst->_neighbour);
+}
+
+static inline struct neighbour *dst_get_neighbour_raw(struct dst_entry *dst)
+{
+       return rcu_dereference_raw(dst->_neighbour);
 }
 
 static inline void dst_set_neighbour(struct dst_entry *dst, struct neighbour *neigh)
 {
-       dst->_neighbour = neigh;
+       rcu_assign_pointer(dst->_neighbour, neigh);
 }
 
 extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old);
@@ -382,8 +387,12 @@ static inline void dst_rcu_free(struct rcu_head *head)
 static inline void dst_confirm(struct dst_entry *dst)
 {
        if (dst) {
-               struct neighbour *n = dst_get_neighbour(dst);
+               struct neighbour *n;
+
+               rcu_read_lock();
+               n = dst_get_neighbour(dst);
                neigh_confirm(n);
+               rcu_read_unlock();
        }
 }
 
index f21a16ee3705325b3830811d0b92eb37ba75dc88..f67440970d7e78d7177252eeafbaead56b74f6dd 100644 (file)
@@ -4,7 +4,7 @@
  * The NetLabel system manages static and dynamic label mappings for network
  * protocols such as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index 6363193a3418e9ee169cd8d2b3011d0970041646..b50a54736242ca21d19a814ab37b27783216397a 100644 (file)
@@ -23,7 +23,7 @@ TRACE_EVENT(ext4_free_inode,
        TP_STRUCT__entry(
                __field(        dev_t,  dev                     )
                __field(        ino_t,  ino                     )
-               __field(        umode_t, mode                   )
+               __field(        __u16, mode                     )
                __field(        uid_t,  uid                     )
                __field(        gid_t,  gid                     )
                __field(        __u64, blocks                   )
@@ -52,7 +52,7 @@ TRACE_EVENT(ext4_request_inode,
        TP_STRUCT__entry(
                __field(        dev_t,  dev                     )
                __field(        ino_t,  dir                     )
-               __field(        umode_t, mode                   )
+               __field(        __u16, mode                     )
        ),
 
        TP_fast_assign(
@@ -75,7 +75,7 @@ TRACE_EVENT(ext4_allocate_inode,
                __field(        dev_t,  dev                     )
                __field(        ino_t,  ino                     )
                __field(        ino_t,  dir                     )
-               __field(        umode_t, mode                   )
+               __field(        __u16,  mode                    )
        ),
 
        TP_fast_assign(
@@ -725,7 +725,7 @@ TRACE_EVENT(ext4_free_blocks,
        TP_STRUCT__entry(
                __field(        dev_t,  dev                     )
                __field(        ino_t,  ino                     )
-               __field(        umode_t, mode                   )
+               __field(        __u16,  mode                    )
                __field(        __u64,  block                   )
                __field(        unsigned long,  count           )
                __field(        int,    flags                   )
@@ -1012,7 +1012,7 @@ TRACE_EVENT(ext4_forget,
        TP_STRUCT__entry(
                __field(        dev_t,  dev                     )
                __field(        ino_t,  ino                     )
-               __field(        umode_t, mode                   )
+               __field(        __u16,  mode                    )
                __field(        int,    is_metadata             )
                __field(        __u64,  block                   )
        ),
@@ -1039,7 +1039,7 @@ TRACE_EVENT(ext4_da_update_reserve_space,
        TP_STRUCT__entry(
                __field(        dev_t,  dev                     )
                __field(        ino_t,  ino                     )
-               __field(        umode_t, mode                   )
+               __field(        __u16,  mode                    )
                __field(        __u64,  i_blocks                )
                __field(        int,    used_blocks             )
                __field(        int,    reserved_data_blocks    )
@@ -1076,7 +1076,7 @@ TRACE_EVENT(ext4_da_reserve_space,
        TP_STRUCT__entry(
                __field(        dev_t,  dev                     )
                __field(        ino_t,  ino                     )
-               __field(        umode_t, mode                   )
+               __field(        __u16,  mode                    )
                __field(        __u64,  i_blocks                )
                __field(        int,    md_needed               )
                __field(        int,    reserved_data_blocks    )
@@ -1110,7 +1110,7 @@ TRACE_EVENT(ext4_da_release_space,
        TP_STRUCT__entry(
                __field(        dev_t,  dev                     )
                __field(        ino_t,  ino                     )
-               __field(        umode_t, mode                   )
+               __field(        __u16,  mode                    )
                __field(        __u64,  i_blocks                )
                __field(        int,    freed_blocks            )
                __field(        int,    reserved_data_blocks    )
@@ -1518,6 +1518,77 @@ TRACE_EVENT(ext4_load_inode,
                  (unsigned long) __entry->ino)
 );
 
+TRACE_EVENT(ext4_journal_start,
+       TP_PROTO(struct super_block *sb, int nblocks, unsigned long IP),
+
+       TP_ARGS(sb, nblocks, IP),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(          int,  nblocks                 )
+               __field(unsigned long,  ip                      )
+       ),
+
+       TP_fast_assign(
+               __entry->dev     = sb->s_dev;
+               __entry->nblocks = nblocks;
+               __entry->ip      = IP;
+       ),
+
+       TP_printk("dev %d,%d nblocks %d caller %pF",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->nblocks, (void *)__entry->ip)
+);
+
+DECLARE_EVENT_CLASS(ext4__trim,
+       TP_PROTO(struct super_block *sb,
+                ext4_group_t group,
+                ext4_grpblk_t start,
+                ext4_grpblk_t len),
+
+       TP_ARGS(sb, group, start, len),
+
+       TP_STRUCT__entry(
+               __field(        int,    dev_major               )
+               __field(        int,    dev_minor               )
+               __field(        __u32,  group                   )
+               __field(        int,    start                   )
+               __field(        int,    len                     )
+       ),
+
+       TP_fast_assign(
+               __entry->dev_major      = MAJOR(sb->s_dev);
+               __entry->dev_minor      = MINOR(sb->s_dev);
+               __entry->group          = group;
+               __entry->start          = start;
+               __entry->len            = len;
+       ),
+
+       TP_printk("dev %d,%d group %u, start %d, len %d",
+                 __entry->dev_major, __entry->dev_minor,
+                 __entry->group, __entry->start, __entry->len)
+);
+
+DEFINE_EVENT(ext4__trim, ext4_trim_extent,
+
+       TP_PROTO(struct super_block *sb,
+                ext4_group_t group,
+                ext4_grpblk_t start,
+                ext4_grpblk_t len),
+
+       TP_ARGS(sb, group, start, len)
+);
+
+DEFINE_EVENT(ext4__trim, ext4_trim_all_free,
+
+       TP_PROTO(struct super_block *sb,
+                ext4_group_t group,
+                ext4_grpblk_t start,
+                ext4_grpblk_t len),
+
+       TP_ARGS(sb, group, start, len)
+);
+
 #endif /* _TRACE_EXT4_H */
 
 /* This part must be outside protection */
index bf16545cc97756d263305f17712f522b979bbf32..75964412ddbb56574d11f8d3e608577bcc21687f 100644 (file)
@@ -26,8 +26,8 @@ TRACE_EVENT(jbd2_checkpoint,
                __entry->result         = result;
        ),
 
-       TP_printk("dev %s result %d",
-                 jbd2_dev_to_name(__entry->dev), __entry->result)
+       TP_printk("dev %d,%d result %d",
+                 MAJOR(__entry->dev), MINOR(__entry->dev), __entry->result)
 );
 
 DECLARE_EVENT_CLASS(jbd2_commit,
@@ -48,9 +48,9 @@ DECLARE_EVENT_CLASS(jbd2_commit,
                __entry->transaction    = commit_transaction->t_tid;
        ),
 
-       TP_printk("dev %s transaction %d sync %d",
-                 jbd2_dev_to_name(__entry->dev), __entry->transaction,
-                 __entry->sync_commit)
+       TP_printk("dev %d,%d transaction %d sync %d",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->transaction, __entry->sync_commit)
 );
 
 DEFINE_EVENT(jbd2_commit, jbd2_start_commit,
@@ -100,9 +100,9 @@ TRACE_EVENT(jbd2_end_commit,
                __entry->head           = journal->j_tail_sequence;
        ),
 
-       TP_printk("dev %s transaction %d sync %d head %d",
-                 jbd2_dev_to_name(__entry->dev), __entry->transaction,
-                 __entry->sync_commit, __entry->head)
+       TP_printk("dev %d,%d transaction %d sync %d head %d",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->transaction, __entry->sync_commit, __entry->head)
 );
 
 TRACE_EVENT(jbd2_submit_inode_data,
@@ -120,8 +120,9 @@ TRACE_EVENT(jbd2_submit_inode_data,
                __entry->ino    = inode->i_ino;
        ),
 
-       TP_printk("dev %s ino %lu",
-                 jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino)
+       TP_printk("dev %d,%d ino %lu",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 (unsigned long) __entry->ino)
 );
 
 TRACE_EVENT(jbd2_run_stats,
@@ -156,9 +157,9 @@ TRACE_EVENT(jbd2_run_stats,
                __entry->blocks_logged  = stats->rs_blocks_logged;
        ),
 
-       TP_printk("dev %s tid %lu wait %u running %u locked %u flushing %u "
+       TP_printk("dev %d,%d tid %lu wait %u running %u locked %u flushing %u "
                  "logging %u handle_count %u blocks %u blocks_logged %u",
-                 jbd2_dev_to_name(__entry->dev), __entry->tid,
+                 MAJOR(__entry->dev), MINOR(__entry->dev), __entry->tid,
                  jiffies_to_msecs(__entry->wait),
                  jiffies_to_msecs(__entry->running),
                  jiffies_to_msecs(__entry->locked),
@@ -192,9 +193,9 @@ TRACE_EVENT(jbd2_checkpoint_stats,
                __entry->dropped        = stats->cs_dropped;
        ),
 
-       TP_printk("dev %s tid %lu chp_time %u forced_to_close %u "
+       TP_printk("dev %d,%d tid %lu chp_time %u forced_to_close %u "
                  "written %u dropped %u",
-                 jbd2_dev_to_name(__entry->dev), __entry->tid,
+                 MAJOR(__entry->dev), MINOR(__entry->dev), __entry->tid,
                  jiffies_to_msecs(__entry->chp_time),
                  __entry->forced_to_close, __entry->written, __entry->dropped)
 );
@@ -222,9 +223,10 @@ TRACE_EVENT(jbd2_cleanup_journal_tail,
                __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)
+       TP_printk("dev %d,%d from %u to %u offset %lu freed %lu",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->tail_sequence, __entry->first_tid,
+                 __entry->block_nr, __entry->freed)
 );
 
 #endif /* _TRACE_JBD2_H */
index d7211faed2adfb295caf46bbb9c70835f622eabe..9c51ee7adf3d7edb01bfc501488389d4ca1c45fc 100644 (file)
@@ -369,9 +369,12 @@ static noinline void __init_refok rest_init(void)
        init_idle_bootup_task(current);
        preempt_enable_no_resched();
        schedule();
-       preempt_disable();
+
+       /* At this point, we can enable user mode helper functionality */
+       usermodehelper_enable();
 
        /* Call into cpu_idle with preempt disabled */
+       preempt_disable();
        cpu_idle();
 }
 
@@ -715,7 +718,7 @@ static void __init do_basic_setup(void)
 {
        cpuset_init_smp();
        usermodehelper_init();
-       init_tmpfs();
+       shmem_init();
        driver_init();
        init_irq_proc();
        do_ctors();
index 9fb044f3b345e5e61ea072e255ab3b67b5ce3539..02ecf2c078fce9b62ee2b1ec0ef252c0320acdf5 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -105,9 +105,16 @@ void shm_exit_ns(struct ipc_namespace *ns)
 }
 #endif
 
-void __init shm_init (void)
+static int __init ipc_ns_init(void)
 {
        shm_init_ns(&init_ipc_ns);
+       return 0;
+}
+
+pure_initcall(ipc_ns_init);
+
+void __init shm_init (void)
+{
        ipc_init_proc_interface("sysvipc/shm",
 #if BITS_PER_LONG <= 32
                                "       key      shmid perms       size  cpid  lpid nattch   uid   gid  cuid  cgid      atime      dtime      ctime        rss       swap\n",
@@ -294,7 +301,7 @@ static int shm_try_destroy_orphaned(int id, void *p, void *data)
 void shm_destroy_orphaned(struct ipc_namespace *ns)
 {
        down_write(&shm_ids(ns).rw_mutex);
-       if (&shm_ids(ns).in_use)
+       if (shm_ids(ns).in_use)
                idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_orphaned, ns);
        up_write(&shm_ids(ns).rw_mutex);
 }
@@ -304,9 +311,12 @@ void exit_shm(struct task_struct *task)
 {
        struct ipc_namespace *ns = task->nsproxy->ipc_ns;
 
+       if (shm_ids(ns).in_use == 0)
+               return;
+
        /* Destroy all already created segments, but not mapped yet */
        down_write(&shm_ids(ns).rw_mutex);
-       if (&shm_ids(ns).in_use)
+       if (shm_ids(ns).in_use)
                idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_current, ns);
        up_write(&shm_ids(ns).rw_mutex);
 }
index a11db956dd62c4c5a5fa22699ccd3662a88f00bc..34872482315e28eade2a7a1d92bfcb0e6cdd5813 100644 (file)
@@ -42,6 +42,8 @@
 /* Our I/O buffers. */
 static char                    remcom_in_buffer[BUFMAX];
 static char                    remcom_out_buffer[BUFMAX];
+static int                     gdbstub_use_prev_in_buf;
+static int                     gdbstub_prev_in_buf_pos;
 
 /* Storage for the registers, in GDB format. */
 static unsigned long           gdb_regs[(NUMREGBYTES +
@@ -58,6 +60,13 @@ static int gdbstub_read_wait(void)
        int ret = -1;
        int i;
 
+       if (unlikely(gdbstub_use_prev_in_buf)) {
+               if (gdbstub_prev_in_buf_pos < gdbstub_use_prev_in_buf)
+                       return remcom_in_buffer[gdbstub_prev_in_buf_pos++];
+               else
+                       gdbstub_use_prev_in_buf = 0;
+       }
+
        /* poll any additional I/O interfaces that are defined */
        while (ret < 0)
                for (i = 0; kdb_poll_funcs[i] != NULL; i++) {
@@ -109,7 +118,6 @@ static void get_packet(char *buffer)
                        buffer[count] = ch;
                        count = count + 1;
                }
-               buffer[count] = 0;
 
                if (ch == '#') {
                        xmitcsum = hex_to_bin(gdbstub_read_wait()) << 4;
@@ -124,6 +132,7 @@ static void get_packet(char *buffer)
                        if (dbg_io_ops->flush)
                                dbg_io_ops->flush();
                }
+               buffer[count] = 0;
        } while (checksum != xmitcsum);
 }
 
@@ -1082,12 +1091,11 @@ int gdbstub_state(struct kgdb_state *ks, char *cmd)
        case 'c':
                strcpy(remcom_in_buffer, cmd);
                return 0;
-       case '?':
-               gdb_cmd_status(ks);
-               break;
-       case '\0':
-               strcpy(remcom_out_buffer, "");
-               break;
+       case '$':
+               strcpy(remcom_in_buffer, cmd);
+               gdbstub_use_prev_in_buf = strlen(remcom_in_buffer);
+               gdbstub_prev_in_buf_pos = 0;
+               return 0;
        }
        dbg_io_ops->write_char('+');
        put_packet(remcom_out_buffer);
index 2f62fe85f16a87e2094df61f63f6e774d8ea1518..7179eac7b41cbf6e8d43f423d8a9ac0e8018efc9 100644 (file)
@@ -112,9 +112,8 @@ kdb_bt(int argc, const char **argv)
        unsigned long addr;
        long offset;
 
-       kdbgetintenv("BTARGS", &argcount);      /* Arguments to print */
-       kdbgetintenv("BTAPROMPT", &btaprompt);  /* Prompt after each
-                                                * proc in bta */
+       /* Prompt after each proc in bta */
+       kdbgetintenv("BTAPROMPT", &btaprompt);
 
        if (strcmp(argv[0], "bta") == 0) {
                struct task_struct *g, *p;
index 56c88e4db309e4972fc7ec1609d629f78b752b33..9834ad303ab6f4e4df59c884bb8cc7ac42a72180 100644 (file)
@@ -18,16 +18,12 @@ defcmd dumpcommon "" "Common kdb debugging"
 endefcmd
 
 defcmd dumpall "" "First line debugging"
-  set BTSYMARG 1
-  set BTARGS 9
   pid R
   -dumpcommon
   -bta
 endefcmd
 
 defcmd dumpcpu "" "Same as dumpall but only tasks on cpus"
-  set BTSYMARG 1
-  set BTARGS 9
   pid R
   -dumpcommon
   -btc
index dd0b1b7dd02c9bb5d8d32b84c39f0a62a4db9c56..d9ca9aa481ecec52edc5a7b41ef000cabe6c5af6 100644 (file)
@@ -30,6 +30,8 @@ EXPORT_SYMBOL_GPL(kdb_poll_funcs);
 int kdb_poll_idx = 1;
 EXPORT_SYMBOL_GPL(kdb_poll_idx);
 
+static struct kgdb_state *kdb_ks;
+
 int kdb_stub(struct kgdb_state *ks)
 {
        int error = 0;
@@ -39,6 +41,7 @@ int kdb_stub(struct kgdb_state *ks)
        kdb_dbtrap_t db_result = KDB_DB_NOBPT;
        int i;
 
+       kdb_ks = ks;
        if (KDB_STATE(REENTRY)) {
                reason = KDB_REASON_SWITCH;
                KDB_STATE_CLEAR(REENTRY);
@@ -123,20 +126,8 @@ int kdb_stub(struct kgdb_state *ks)
        KDB_STATE_CLEAR(PAGER);
        kdbnearsym_cleanup();
        if (error == KDB_CMD_KGDB) {
-               if (KDB_STATE(DOING_KGDB) || KDB_STATE(DOING_KGDB2)) {
-       /*
-        * This inteface glue which allows kdb to transition in into
-        * the gdb stub.  In order to do this the '?' or '' gdb serial
-        * packet response is processed here.  And then control is
-        * passed to the gdbstub.
-        */
-                       if (KDB_STATE(DOING_KGDB))
-                               gdbstub_state(ks, "?");
-                       else
-                               gdbstub_state(ks, "");
+               if (KDB_STATE(DOING_KGDB))
                        KDB_STATE_CLEAR(DOING_KGDB);
-                       KDB_STATE_CLEAR(DOING_KGDB2);
-               }
                return DBG_PASS_EVENT;
        }
        kdb_bp_install(ks->linux_regs);
@@ -166,3 +157,7 @@ int kdb_stub(struct kgdb_state *ks)
        return kgdb_info[ks->cpu].ret_state;
 }
 
+void kdb_gdb_state_pass(char *buf)
+{
+       gdbstub_state(kdb_ks, buf);
+}
index 96fdaac46a80b03c41e79b95a8c7e521ad437021..4802eb5840e1a138057444fbb7e1a7953a507d96 100644 (file)
@@ -31,15 +31,21 @@ char kdb_prompt_str[CMD_BUFLEN];
 
 int kdb_trap_printk;
 
-static void kgdb_transition_check(char *buffer)
+static int kgdb_transition_check(char *buffer)
 {
-       int slen = strlen(buffer);
-       if (strncmp(buffer, "$?#3f", slen) != 0 &&
-           strncmp(buffer, "$qSupported#37", slen) != 0 &&
-           strncmp(buffer, "+$qSupported#37", slen) != 0) {
+       if (buffer[0] != '+' && buffer[0] != '$') {
                KDB_STATE_SET(KGDB_TRANS);
                kdb_printf("%s", buffer);
+       } else {
+               int slen = strlen(buffer);
+               if (slen > 3 && buffer[slen - 3] == '#') {
+                       kdb_gdb_state_pass(buffer);
+                       strcpy(buffer, "kgdb");
+                       KDB_STATE_SET(DOING_KGDB);
+                       return 1;
+               }
        }
+       return 0;
 }
 
 static int kdb_read_get_key(char *buffer, size_t bufsize)
@@ -251,6 +257,10 @@ poll_again:
        case 13: /* enter */
                *lastchar++ = '\n';
                *lastchar++ = '\0';
+               if (!KDB_STATE(KGDB_TRANS)) {
+                       KDB_STATE_SET(KGDB_TRANS);
+                       kdb_printf("%s", buffer);
+               }
                kdb_printf("\n");
                return buffer;
        case 4: /* Del */
@@ -382,22 +392,26 @@ poll_again:
                                 * printed characters if we think that
                                 * kgdb is connecting, until the check
                                 * fails */
-                               if (!KDB_STATE(KGDB_TRANS))
-                                       kgdb_transition_check(buffer);
-                               else
+                               if (!KDB_STATE(KGDB_TRANS)) {
+                                       if (kgdb_transition_check(buffer))
+                                               return buffer;
+                               } else {
                                        kdb_printf("%c", key);
+                               }
                        }
                        /* Special escape to kgdb */
                        if (lastchar - buffer >= 5 &&
                            strcmp(lastchar - 5, "$?#3f") == 0) {
+                               kdb_gdb_state_pass(lastchar - 5);
                                strcpy(buffer, "kgdb");
                                KDB_STATE_SET(DOING_KGDB);
                                return buffer;
                        }
-                       if (lastchar - buffer >= 14 &&
-                           strcmp(lastchar - 14, "$qSupported#37") == 0) {
+                       if (lastchar - buffer >= 11 &&
+                           strcmp(lastchar - 11, "$qSupported") == 0) {
+                               kdb_gdb_state_pass(lastchar - 11);
                                strcpy(buffer, "kgdb");
-                               KDB_STATE_SET(DOING_KGDB2);
+                               KDB_STATE_SET(DOING_KGDB);
                                return buffer;
                        }
                }
index be14779bcef66751bbbacc020c166f90ab38ff59..63786e71a3cd9e1003a7a2bb6f399170f773756d 100644 (file)
@@ -145,7 +145,6 @@ static char *__env[] = {
 #endif
  "RADIX=16",
  "MDCOUNT=8",                  /* lines of md output */
- "BTARGS=9",                   /* 9 possible args in bt */
  KDB_PLATFORM_ENV,
  "DTABCOUNT=30",
  "NOSECT=1",
@@ -172,6 +171,7 @@ static char *__env[] = {
  (char *)0,
  (char *)0,
  (char *)0,
+ (char *)0,
 };
 
 static const int __nenv = (sizeof(__env) / sizeof(char *));
@@ -1386,7 +1386,7 @@ int kdb_main_loop(kdb_reason_t reason, kdb_reason_t reason2, int error,
                }
 
                if (result == KDB_CMD_KGDB) {
-                       if (!(KDB_STATE(DOING_KGDB) || KDB_STATE(DOING_KGDB2)))
+                       if (!KDB_STATE(DOING_KGDB))
                                kdb_printf("Entering please attach debugger "
                                           "or use $D#44+ or $3#33\n");
                        break;
index 35d69ed1dfb58e388dcd8717776631aa4473802d..e381d105b40b827c71660d6f5265d197dba18c9f 100644 (file)
@@ -21,7 +21,6 @@
 #define KDB_CMD_SS     (-1003)
 #define KDB_CMD_SSB    (-1004)
 #define KDB_CMD_KGDB (-1005)
-#define KDB_CMD_KGDB2 (-1006)
 
 /* Internal debug flags */
 #define KDB_DEBUG_FLAG_BP      0x0002  /* Breakpoint subsystem debug */
@@ -146,7 +145,6 @@ extern int kdb_state;
                                                 * keyboard on this cpu */
 #define KDB_STATE_KEXEC                0x00040000      /* kexec issued */
 #define KDB_STATE_DOING_KGDB   0x00080000      /* kgdb enter now issued */
-#define KDB_STATE_DOING_KGDB2  0x00100000      /* kgdb enter now issued */
 #define KDB_STATE_KGDB_TRANS   0x00200000      /* Transition to kgdb */
 #define KDB_STATE_ARCH         0xff000000      /* Reserved for arch
                                                 * specific use */
@@ -218,6 +216,7 @@ extern void kdb_print_nameval(const char *name, unsigned long val);
 extern void kdb_send_sig_info(struct task_struct *p, struct siginfo *info);
 extern void kdb_meminfo_proc_show(void);
 extern char *kdb_getstr(char *, size_t, char *);
+extern void kdb_gdb_state_pass(char *buf);
 
 /* Defines for kdb_symbol_print */
 #define KDB_SP_SPACEB  0x0001          /* Space before string */
index 0a308970c24ac4ce1ef00256df7f8afcf2018044..11cbe052b2e8bb571c49fc7ebcf1268866c0a8b4 100644 (file)
@@ -218,6 +218,8 @@ static void drop_futex_key_refs(union futex_key *key)
  * @uaddr:     virtual address of the futex
  * @fshared:   0 for a PROCESS_PRIVATE futex, 1 for PROCESS_SHARED
  * @key:       address where result is stored.
+ * @rw:                mapping needs to be read/write (values: VERIFY_READ,
+ *              VERIFY_WRITE)
  *
  * Returns a negative error code or 0
  * The key words are stored in *key on success.
@@ -229,12 +231,12 @@ static void drop_futex_key_refs(union futex_key *key)
  * lock_page() might sleep, the caller should not hold a spinlock.
  */
 static int
-get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
+get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw)
 {
        unsigned long address = (unsigned long)uaddr;
        struct mm_struct *mm = current->mm;
        struct page *page, *page_head;
-       int err;
+       int err, ro = 0;
 
        /*
         * The futex address must be "naturally" aligned.
@@ -262,8 +264,18 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
 
 again:
        err = get_user_pages_fast(address, 1, 1, &page);
+       /*
+        * If write access is not required (eg. FUTEX_WAIT), try
+        * and get read-only access.
+        */
+       if (err == -EFAULT && rw == VERIFY_READ) {
+               err = get_user_pages_fast(address, 1, 0, &page);
+               ro = 1;
+       }
        if (err < 0)
                return err;
+       else
+               err = 0;
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
        page_head = page;
@@ -305,6 +317,13 @@ again:
        if (!page_head->mapping) {
                unlock_page(page_head);
                put_page(page_head);
+               /*
+               * ZERO_PAGE pages don't have a mapping. Avoid a busy loop
+               * trying to find one. RW mapping would have COW'd (and thus
+               * have a mapping) so this page is RO and won't ever change.
+               */
+               if ((page_head == ZERO_PAGE(address)))
+                       return -EFAULT;
                goto again;
        }
 
@@ -316,6 +335,15 @@ again:
         * the object not the particular process.
         */
        if (PageAnon(page_head)) {
+               /*
+                * A RO anonymous page will never change and thus doesn't make
+                * sense for futex operations.
+                */
+               if (ro) {
+                       err = -EFAULT;
+                       goto out;
+               }
+
                key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */
                key->private.mm = mm;
                key->private.address = address;
@@ -327,9 +355,10 @@ again:
 
        get_futex_key_refs(key);
 
+out:
        unlock_page(page_head);
        put_page(page_head);
-       return 0;
+       return err;
 }
 
 static inline void put_futex_key(union futex_key *key)
@@ -940,7 +969,7 @@ futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)
        if (!bitset)
                return -EINVAL;
 
-       ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key);
+       ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key, VERIFY_READ);
        if (unlikely(ret != 0))
                goto out;
 
@@ -986,10 +1015,10 @@ futex_wake_op(u32 __user *uaddr1, unsigned int flags, u32 __user *uaddr2,
        int ret, op_ret;
 
 retry:
-       ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1);
+       ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1, VERIFY_READ);
        if (unlikely(ret != 0))
                goto out;
-       ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2);
+       ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE);
        if (unlikely(ret != 0))
                goto out_put_key1;
 
@@ -1243,10 +1272,11 @@ retry:
                pi_state = NULL;
        }
 
-       ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1);
+       ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1, VERIFY_READ);
        if (unlikely(ret != 0))
                goto out;
-       ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2);
+       ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2,
+                           requeue_pi ? VERIFY_WRITE : VERIFY_READ);
        if (unlikely(ret != 0))
                goto out_put_key1;
 
@@ -1790,7 +1820,7 @@ static int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
         * while the syscall executes.
         */
 retry:
-       ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q->key);
+       ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q->key, VERIFY_READ);
        if (unlikely(ret != 0))
                return ret;
 
@@ -1941,7 +1971,7 @@ static int futex_lock_pi(u32 __user *uaddr, unsigned int flags, int detect,
        }
 
 retry:
-       ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q.key);
+       ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q.key, VERIFY_WRITE);
        if (unlikely(ret != 0))
                goto out;
 
@@ -2060,7 +2090,7 @@ retry:
        if ((uval & FUTEX_TID_MASK) != vpid)
                return -EPERM;
 
-       ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key);
+       ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key, VERIFY_WRITE);
        if (unlikely(ret != 0))
                goto out;
 
@@ -2249,7 +2279,7 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
        debug_rt_mutex_init_waiter(&rt_waiter);
        rt_waiter.task = NULL;
 
-       ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2);
+       ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE);
        if (unlikely(ret != 0))
                goto out;
 
index 47613dfb7b28c340825493ed36a4c3347a39290d..ddc7644c1305cf926364b0d1682e5d668367f08b 100644 (file)
@@ -274,7 +274,7 @@ static void __call_usermodehelper(struct work_struct *work)
  * (used for preventing user land processes from being created after the user
  * land has been frozen during a system-wide hibernation or suspend operation).
  */
-static int usermodehelper_disabled;
+static int usermodehelper_disabled = 1;
 
 /* Number of helpers running */
 static atomic_t running_helpers = ATOMIC_INIT(0);
index 3956f5149e25c2664876c081d8befc9814c1be4e..8c24294e477fe6578cb16ae8c3e270b0633cc6c7 100644 (file)
@@ -2468,7 +2468,7 @@ mark_held_locks(struct task_struct *curr, enum mark_type mark)
 
                BUG_ON(usage_bit >= LOCK_USAGE_STATES);
 
-               if (hlock_class(hlock)->key == &__lockdep_no_validate__)
+               if (hlock_class(hlock)->key == __lockdep_no_validate__.subkeys)
                        continue;
 
                if (!mark_lock(curr, hlock, usage_bit))
@@ -2485,23 +2485,9 @@ static void __trace_hardirqs_on_caller(unsigned long ip)
 {
        struct task_struct *curr = current;
 
-       if (DEBUG_LOCKS_WARN_ON(unlikely(early_boot_irqs_disabled)))
-               return;
-
-       if (unlikely(curr->hardirqs_enabled)) {
-               /*
-                * Neither irq nor preemption are disabled here
-                * so this is racy by nature but losing one hit
-                * in a stat is not a big deal.
-                */
-               __debug_atomic_inc(redundant_hardirqs_on);
-               return;
-       }
        /* we'll do an OFF -> ON transition: */
        curr->hardirqs_enabled = 1;
 
-       if (DEBUG_LOCKS_WARN_ON(current->hardirq_context))
-               return;
        /*
         * We are going to turn hardirqs on, so set the
         * usage bit for all held locks:
@@ -2529,9 +2515,25 @@ void trace_hardirqs_on_caller(unsigned long ip)
        if (unlikely(!debug_locks || current->lockdep_recursion))
                return;
 
+       if (unlikely(current->hardirqs_enabled)) {
+               /*
+                * Neither irq nor preemption are disabled here
+                * so this is racy by nature but losing one hit
+                * in a stat is not a big deal.
+                */
+               __debug_atomic_inc(redundant_hardirqs_on);
+               return;
+       }
+
        if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
                return;
 
+       if (DEBUG_LOCKS_WARN_ON(unlikely(early_boot_irqs_disabled)))
+               return;
+
+       if (DEBUG_LOCKS_WARN_ON(current->hardirq_context))
+               return;
+
        current->lockdep_recursion = 1;
        __trace_hardirqs_on_caller(ip);
        current->lockdep_recursion = 0;
@@ -2872,10 +2874,7 @@ static int mark_lock(struct task_struct *curr, struct held_lock *this,
 void lockdep_init_map(struct lockdep_map *lock, const char *name,
                      struct lock_class_key *key, int subclass)
 {
-       int i;
-
-       for (i = 0; i < NR_LOCKDEP_CACHING_CLASSES; i++)
-               lock->class_cache[i] = NULL;
+       memset(lock, 0, sizeof(*lock));
 
 #ifdef CONFIG_LOCK_STAT
        lock->cpu = raw_smp_processor_id();
index d1db2880d1cf8b3daac8f95f3fd85ac70c1eda7c..e19ce1454ee1d6d2e347e8c287abf2fe68fcc9e1 100644 (file)
@@ -291,30 +291,28 @@ static int add_del_listener(pid_t pid, const struct cpumask *mask, int isadd)
        if (!cpumask_subset(mask, cpu_possible_mask))
                return -EINVAL;
 
-       s = NULL;
        if (isadd == REGISTER) {
                for_each_cpu(cpu, mask) {
-                       if (!s)
-                               s = kmalloc_node(sizeof(struct listener),
-                                                GFP_KERNEL, cpu_to_node(cpu));
+                       s = kmalloc_node(sizeof(struct listener),
+                                       GFP_KERNEL, cpu_to_node(cpu));
                        if (!s)
                                goto cleanup;
+
                        s->pid = pid;
-                       INIT_LIST_HEAD(&s->list);
                        s->valid = 1;
 
                        listeners = &per_cpu(listener_array, cpu);
                        down_write(&listeners->sem);
-                       list_for_each_entry_safe(s2, tmp, &listeners->list, list) {
-                               if (s2->pid == pid)
-                                       goto next_cpu;
+                       list_for_each_entry(s2, &listeners->list, list) {
+                               if (s2->pid == pid && s2->valid)
+                                       goto exists;
                        }
                        list_add(&s->list, &listeners->list);
                        s = NULL;
-next_cpu:
+exists:
                        up_write(&listeners->sem);
+                       kfree(s); /* nop if NULL */
                }
-               kfree(s);
                return 0;
        }
 
index 32f3e5ae2be543c59ae3e50682ca504010eb87a3..6c695ff9caba7579aaf9f4814abff70ba7c21084 100644 (file)
@@ -276,4 +276,7 @@ config CORDIC
          so its calculations are in fixed point. Modules can select this
          when they require this function. Module will be called cordic.
 
+config LLIST
+       bool
+
 endmenu
index 892f4e282ea1c0aecf8a60c33ec8c6bde2d2c175..6457af4a7caf632d9bd26c70ac4b5744199e5079 100644 (file)
@@ -115,6 +115,8 @@ obj-$(CONFIG_CPU_RMAP) += cpu_rmap.o
 
 obj-$(CONFIG_CORDIC) += cordic.o
 
+obj-$(CONFIG_LLIST) += llist.o
+
 hostprogs-y    := gen_crc32table
 clean-files    := crc32table.h
 
index 37ef4b048795463a46a98702fd5bf780c1b59060..2f4412e4d0719c25ca1d4948d489eee8c998f444 100644 (file)
@@ -271,8 +271,6 @@ int __bitmap_weight(const unsigned long *bitmap, int bits)
 }
 EXPORT_SYMBOL(__bitmap_weight);
 
-#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG))
-
 void bitmap_set(unsigned long *map, int start, int nr)
 {
        unsigned long *p = map + BIT_WORD(start);
index 2577b121c7c1e5ef4413777d928b023ed52d42ac..f193b7796449a04870b401f96931ef71c18f808d 100644 (file)
@@ -197,21 +197,15 @@ static struct dentry *debugfs_create_atomic_t(const char *name, mode_t mode,
        return debugfs_create_file(name, mode, parent, value, &fops_atomic_t);
 }
 
-void cleanup_fault_attr_dentries(struct fault_attr *attr)
-{
-       debugfs_remove_recursive(attr->dir);
-}
-
-int init_fault_attr_dentries(struct fault_attr *attr, const char *name)
+struct dentry *fault_create_debugfs_attr(const char *name,
+                       struct dentry *parent, struct fault_attr *attr)
 {
        mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
        struct dentry *dir;
 
-       dir = debugfs_create_dir(name, NULL);
+       dir = debugfs_create_dir(name, parent);
        if (!dir)
-               return -ENOMEM;
-
-       attr->dir = dir;
+               return ERR_PTR(-ENOMEM);
 
        if (!debugfs_create_ul("probability", mode, dir, &attr->probability))
                goto fail;
@@ -243,11 +237,11 @@ int init_fault_attr_dentries(struct fault_attr *attr, const char *name)
 
 #endif /* CONFIG_FAULT_INJECTION_STACKTRACE_FILTER */
 
-       return 0;
+       return dir;
 fail:
-       debugfs_remove_recursive(attr->dir);
+       debugfs_remove_recursive(dir);
 
-       return -ENOMEM;
+       return ERR_PTR(-ENOMEM);
 }
 
 #endif /* CONFIG_FAULT_INJECTION_DEBUG_FS */
index 577ddf8059758b05ecf9871c2409e3c8d346b5c6..f352cc42f4f8a58b8e4c8304de32cb31b4e29b67 100644 (file)
@@ -1,8 +1,26 @@
 /*
- * Basic general purpose allocator for managing special purpose memory
- * not managed by the regular kmalloc/kfree interface.
- * Uses for this includes on-device special memory, uncached memory
- * etc.
+ * Basic general purpose allocator for managing special purpose
+ * memory, for example, memory that is not managed by the regular
+ * kmalloc/kfree interface.  Uses for this includes on-device special
+ * memory, uncached memory etc.
+ *
+ * It is safe to use the allocator in NMI handlers and other special
+ * unblockable contexts that could otherwise deadlock on locks.  This
+ * is implemented by using atomic operations and retries on any
+ * conflicts.  The disadvantage is that there may be livelocks in
+ * extreme cases.  For better scalability, one allocator can be used
+ * for each CPU.
+ *
+ * The lockless operation only works if there is enough memory
+ * available.  If new memory is added to the pool a lock has to be
+ * still taken.  So any user relying on locklessness has to ensure
+ * that sufficient memory is preallocated.
+ *
+ * The basic atomic operation of this allocator is cmpxchg on long.
+ * On architectures that don't have NMI-safe cmpxchg implementation,
+ * the allocator can NOT be used in NMI handler.  So code uses the
+ * allocator in NMI handler should depend on
+ * CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG.
  *
  * Copyright 2005 (C) Jes Sorensen <jes@trained-monkey.org>
  *
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/bitmap.h>
+#include <linux/rculist.h>
+#include <linux/interrupt.h>
 #include <linux/genalloc.h>
 
+static int set_bits_ll(unsigned long *addr, unsigned long mask_to_set)
+{
+       unsigned long val, nval;
+
+       nval = *addr;
+       do {
+               val = nval;
+               if (val & mask_to_set)
+                       return -EBUSY;
+               cpu_relax();
+       } while ((nval = cmpxchg(addr, val, val | mask_to_set)) != val);
+
+       return 0;
+}
+
+static int clear_bits_ll(unsigned long *addr, unsigned long mask_to_clear)
+{
+       unsigned long val, nval;
+
+       nval = *addr;
+       do {
+               val = nval;
+               if ((val & mask_to_clear) != mask_to_clear)
+                       return -EBUSY;
+               cpu_relax();
+       } while ((nval = cmpxchg(addr, val, val & ~mask_to_clear)) != val);
+
+       return 0;
+}
+
+/*
+ * bitmap_set_ll - set the specified number of bits at the specified position
+ * @map: pointer to a bitmap
+ * @start: a bit position in @map
+ * @nr: number of bits to set
+ *
+ * Set @nr bits start from @start in @map lock-lessly. Several users
+ * can set/clear the same bitmap simultaneously without lock. If two
+ * users set the same bit, one user will return remain bits, otherwise
+ * return 0.
+ */
+static int bitmap_set_ll(unsigned long *map, int start, int nr)
+{
+       unsigned long *p = map + BIT_WORD(start);
+       const int size = start + nr;
+       int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
+       unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
+
+       while (nr - bits_to_set >= 0) {
+               if (set_bits_ll(p, mask_to_set))
+                       return nr;
+               nr -= bits_to_set;
+               bits_to_set = BITS_PER_LONG;
+               mask_to_set = ~0UL;
+               p++;
+       }
+       if (nr) {
+               mask_to_set &= BITMAP_LAST_WORD_MASK(size);
+               if (set_bits_ll(p, mask_to_set))
+                       return nr;
+       }
+
+       return 0;
+}
+
+/*
+ * bitmap_clear_ll - clear the specified number of bits at the specified position
+ * @map: pointer to a bitmap
+ * @start: a bit position in @map
+ * @nr: number of bits to set
+ *
+ * Clear @nr bits start from @start in @map lock-lessly. Several users
+ * can set/clear the same bitmap simultaneously without lock. If two
+ * users clear the same bit, one user will return remain bits,
+ * otherwise return 0.
+ */
+static int bitmap_clear_ll(unsigned long *map, int start, int nr)
+{
+       unsigned long *p = map + BIT_WORD(start);
+       const int size = start + nr;
+       int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG);
+       unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start);
+
+       while (nr - bits_to_clear >= 0) {
+               if (clear_bits_ll(p, mask_to_clear))
+                       return nr;
+               nr -= bits_to_clear;
+               bits_to_clear = BITS_PER_LONG;
+               mask_to_clear = ~0UL;
+               p++;
+       }
+       if (nr) {
+               mask_to_clear &= BITMAP_LAST_WORD_MASK(size);
+               if (clear_bits_ll(p, mask_to_clear))
+                       return nr;
+       }
+
+       return 0;
+}
 
 /**
  * gen_pool_create - create a new special memory pool
@@ -30,7 +149,7 @@ struct gen_pool *gen_pool_create(int min_alloc_order, int nid)
 
        pool = kmalloc_node(sizeof(struct gen_pool), GFP_KERNEL, nid);
        if (pool != NULL) {
-               rwlock_init(&pool->lock);
+               spin_lock_init(&pool->lock);
                INIT_LIST_HEAD(&pool->chunks);
                pool->min_alloc_order = min_alloc_order;
        }
@@ -63,14 +182,14 @@ int gen_pool_add_virt(struct gen_pool *pool, unsigned long virt, phys_addr_t phy
        if (unlikely(chunk == NULL))
                return -ENOMEM;
 
-       spin_lock_init(&chunk->lock);
        chunk->phys_addr = phys;
        chunk->start_addr = virt;
        chunk->end_addr = virt + size;
+       atomic_set(&chunk->avail, size);
 
-       write_lock(&pool->lock);
-       list_add(&chunk->next_chunk, &pool->chunks);
-       write_unlock(&pool->lock);
+       spin_lock(&pool->lock);
+       list_add_rcu(&chunk->next_chunk, &pool->chunks);
+       spin_unlock(&pool->lock);
 
        return 0;
 }
@@ -85,19 +204,19 @@ EXPORT_SYMBOL(gen_pool_add_virt);
  */
 phys_addr_t gen_pool_virt_to_phys(struct gen_pool *pool, unsigned long addr)
 {
-       struct list_head *_chunk;
        struct gen_pool_chunk *chunk;
+       phys_addr_t paddr = -1;
 
-       read_lock(&pool->lock);
-       list_for_each(_chunk, &pool->chunks) {
-               chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
-
-               if (addr >= chunk->start_addr && addr < chunk->end_addr)
-                       return chunk->phys_addr + addr - chunk->start_addr;
+       rcu_read_lock();
+       list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
+               if (addr >= chunk->start_addr && addr < chunk->end_addr) {
+                       paddr = chunk->phys_addr + (addr - chunk->start_addr);
+                       break;
+               }
        }
-       read_unlock(&pool->lock);
+       rcu_read_unlock();
 
-       return -1;
+       return paddr;
 }
 EXPORT_SYMBOL(gen_pool_virt_to_phys);
 
@@ -115,7 +234,6 @@ void gen_pool_destroy(struct gen_pool *pool)
        int order = pool->min_alloc_order;
        int bit, end_bit;
 
-
        list_for_each_safe(_chunk, _next_chunk, &pool->chunks) {
                chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
                list_del(&chunk->next_chunk);
@@ -137,44 +255,50 @@ EXPORT_SYMBOL(gen_pool_destroy);
  * @size: number of bytes to allocate from the pool
  *
  * Allocate the requested number of bytes from the specified pool.
- * Uses a first-fit algorithm.
+ * Uses a first-fit algorithm. Can not be used in NMI handler on
+ * architectures without NMI-safe cmpxchg implementation.
  */
 unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)
 {
-       struct list_head *_chunk;
        struct gen_pool_chunk *chunk;
-       unsigned long addr, flags;
+       unsigned long addr = 0;
        int order = pool->min_alloc_order;
-       int nbits, start_bit, end_bit;
+       int nbits, start_bit = 0, end_bit, remain;
+
+#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+       BUG_ON(in_nmi());
+#endif
 
        if (size == 0)
                return 0;
 
        nbits = (size + (1UL << order) - 1) >> order;
-
-       read_lock(&pool->lock);
-       list_for_each(_chunk, &pool->chunks) {
-               chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
+       rcu_read_lock();
+       list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
+               if (size > atomic_read(&chunk->avail))
+                       continue;
 
                end_bit = (chunk->end_addr - chunk->start_addr) >> order;
-
-               spin_lock_irqsave(&chunk->lock, flags);
-               start_bit = bitmap_find_next_zero_area(chunk->bits, end_bit, 0,
-                                               nbits, 0);
-               if (start_bit >= end_bit) {
-                       spin_unlock_irqrestore(&chunk->lock, flags);
+retry:
+               start_bit = bitmap_find_next_zero_area(chunk->bits, end_bit,
+                                                      start_bit, nbits, 0);
+               if (start_bit >= end_bit)
                        continue;
+               remain = bitmap_set_ll(chunk->bits, start_bit, nbits);
+               if (remain) {
+                       remain = bitmap_clear_ll(chunk->bits, start_bit,
+                                                nbits - remain);
+                       BUG_ON(remain);
+                       goto retry;
                }
 
                addr = chunk->start_addr + ((unsigned long)start_bit << order);
-
-               bitmap_set(chunk->bits, start_bit, nbits);
-               spin_unlock_irqrestore(&chunk->lock, flags);
-               read_unlock(&pool->lock);
-               return addr;
+               size = nbits << order;
+               atomic_sub(size, &chunk->avail);
+               break;
        }
-       read_unlock(&pool->lock);
-       return 0;
+       rcu_read_unlock();
+       return addr;
 }
 EXPORT_SYMBOL(gen_pool_alloc);
 
@@ -184,33 +308,95 @@ EXPORT_SYMBOL(gen_pool_alloc);
  * @addr: starting address of memory to free back to pool
  * @size: size in bytes of memory to free
  *
- * Free previously allocated special memory back to the specified pool.
+ * Free previously allocated special memory back to the specified
+ * pool.  Can not be used in NMI handler on architectures without
+ * NMI-safe cmpxchg implementation.
  */
 void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size)
 {
-       struct list_head *_chunk;
        struct gen_pool_chunk *chunk;
-       unsigned long flags;
        int order = pool->min_alloc_order;
-       int bit, nbits;
+       int start_bit, nbits, remain;
 
-       nbits = (size + (1UL << order) - 1) >> order;
-
-       read_lock(&pool->lock);
-       list_for_each(_chunk, &pool->chunks) {
-               chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
+#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+       BUG_ON(in_nmi());
+#endif
 
+       nbits = (size + (1UL << order) - 1) >> order;
+       rcu_read_lock();
+       list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
                if (addr >= chunk->start_addr && addr < chunk->end_addr) {
                        BUG_ON(addr + size > chunk->end_addr);
-                       spin_lock_irqsave(&chunk->lock, flags);
-                       bit = (addr - chunk->start_addr) >> order;
-                       while (nbits--)
-                               __clear_bit(bit++, chunk->bits);
-                       spin_unlock_irqrestore(&chunk->lock, flags);
-                       break;
+                       start_bit = (addr - chunk->start_addr) >> order;
+                       remain = bitmap_clear_ll(chunk->bits, start_bit, nbits);
+                       BUG_ON(remain);
+                       size = nbits << order;
+                       atomic_add(size, &chunk->avail);
+                       rcu_read_unlock();
+                       return;
                }
        }
-       BUG_ON(nbits > 0);
-       read_unlock(&pool->lock);
+       rcu_read_unlock();
+       BUG();
 }
 EXPORT_SYMBOL(gen_pool_free);
+
+/**
+ * gen_pool_for_each_chunk - call func for every chunk of generic memory pool
+ * @pool:      the generic memory pool
+ * @func:      func to call
+ * @data:      additional data used by @func
+ *
+ * Call @func for every chunk of generic memory pool.  The @func is
+ * called with rcu_read_lock held.
+ */
+void gen_pool_for_each_chunk(struct gen_pool *pool,
+       void (*func)(struct gen_pool *pool, struct gen_pool_chunk *chunk, void *data),
+       void *data)
+{
+       struct gen_pool_chunk *chunk;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(chunk, &(pool)->chunks, next_chunk)
+               func(pool, chunk, data);
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL(gen_pool_for_each_chunk);
+
+/**
+ * gen_pool_avail - get available free space of the pool
+ * @pool: pool to get available free space
+ *
+ * Return available free space of the specified pool.
+ */
+size_t gen_pool_avail(struct gen_pool *pool)
+{
+       struct gen_pool_chunk *chunk;
+       size_t avail = 0;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk)
+               avail += atomic_read(&chunk->avail);
+       rcu_read_unlock();
+       return avail;
+}
+EXPORT_SYMBOL_GPL(gen_pool_avail);
+
+/**
+ * gen_pool_size - get size in bytes of memory managed by the pool
+ * @pool: pool to get size
+ *
+ * Return size in bytes of memory managed by the pool.
+ */
+size_t gen_pool_size(struct gen_pool *pool)
+{
+       struct gen_pool_chunk *chunk;
+       size_t size = 0;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk)
+               size += chunk->end_addr - chunk->start_addr;
+       rcu_read_unlock();
+       return size;
+}
+EXPORT_SYMBOL_GPL(gen_pool_size);
index e15502e8b21e5bbb47addef5c8fea61b57118508..db040ce3fa73a328a118a93097d6e5062c16409d 100644 (file)
--- a/lib/idr.c
+++ b/lib/idr.c
 #include <linux/err.h>
 #include <linux/string.h>
 #include <linux/idr.h>
+#include <linux/spinlock.h>
 
 static struct kmem_cache *idr_layer_cache;
+static DEFINE_SPINLOCK(simple_ida_lock);
 
 static struct idr_layer *get_from_free_list(struct idr *idp)
 {
@@ -925,6 +927,71 @@ void ida_destroy(struct ida *ida)
 }
 EXPORT_SYMBOL(ida_destroy);
 
+/**
+ * ida_simple_get - get a new id.
+ * @ida: the (initialized) ida.
+ * @start: the minimum id (inclusive, < 0x8000000)
+ * @end: the maximum id (exclusive, < 0x8000000 or 0)
+ * @gfp_mask: memory allocation flags
+ *
+ * Allocates an id in the range start <= id < end, or returns -ENOSPC.
+ * On memory allocation failure, returns -ENOMEM.
+ *
+ * Use ida_simple_remove() to get rid of an id.
+ */
+int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end,
+                  gfp_t gfp_mask)
+{
+       int ret, id;
+       unsigned int max;
+
+       BUG_ON((int)start < 0);
+       BUG_ON((int)end < 0);
+
+       if (end == 0)
+               max = 0x80000000;
+       else {
+               BUG_ON(end < start);
+               max = end - 1;
+       }
+
+again:
+       if (!ida_pre_get(ida, gfp_mask))
+               return -ENOMEM;
+
+       spin_lock(&simple_ida_lock);
+       ret = ida_get_new_above(ida, start, &id);
+       if (!ret) {
+               if (id > max) {
+                       ida_remove(ida, id);
+                       ret = -ENOSPC;
+               } else {
+                       ret = id;
+               }
+       }
+       spin_unlock(&simple_ida_lock);
+
+       if (unlikely(ret == -EAGAIN))
+               goto again;
+
+       return ret;
+}
+EXPORT_SYMBOL(ida_simple_get);
+
+/**
+ * ida_simple_remove - remove an allocated id.
+ * @ida: the (initialized) ida.
+ * @id: the id returned by ida_simple_get.
+ */
+void ida_simple_remove(struct ida *ida, unsigned int id)
+{
+       BUG_ON((int)id < 0);
+       spin_lock(&simple_ida_lock);
+       ida_remove(ida, id);
+       spin_unlock(&simple_ida_lock);
+}
+EXPORT_SYMBOL(ida_simple_remove);
+
 /**
  * ida_init - initialize ida handle
  * @ida:       ida handle
diff --git a/lib/llist.c b/lib/llist.c
new file mode 100644 (file)
index 0000000..da44572
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Lock-less NULL terminated single linked list
+ *
+ * The basic atomic operation of this list is cmpxchg on long.  On
+ * architectures that don't have NMI-safe cmpxchg implementation, the
+ * list can NOT be used in NMI handler.  So code uses the list in NMI
+ * handler should depend on CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG.
+ *
+ * Copyright 2010,2011 Intel Corp.
+ *   Author: Huang Ying <ying.huang@intel.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;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/llist.h>
+
+#include <asm/system.h>
+
+/**
+ * llist_add - add a new entry
+ * @new:       new entry to be added
+ * @head:      the head for your lock-less list
+ */
+void llist_add(struct llist_node *new, struct llist_head *head)
+{
+       struct llist_node *entry, *old_entry;
+
+#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+       BUG_ON(in_nmi());
+#endif
+
+       entry = head->first;
+       do {
+               old_entry = entry;
+               new->next = entry;
+               cpu_relax();
+       } while ((entry = cmpxchg(&head->first, old_entry, new)) != old_entry);
+}
+EXPORT_SYMBOL_GPL(llist_add);
+
+/**
+ * llist_add_batch - add several linked entries in batch
+ * @new_first: first entry in batch to be added
+ * @new_last:  last entry in batch to be added
+ * @head:      the head for your lock-less list
+ */
+void llist_add_batch(struct llist_node *new_first, struct llist_node *new_last,
+                    struct llist_head *head)
+{
+       struct llist_node *entry, *old_entry;
+
+#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+       BUG_ON(in_nmi());
+#endif
+
+       entry = head->first;
+       do {
+               old_entry = entry;
+               new_last->next = entry;
+               cpu_relax();
+       } while ((entry = cmpxchg(&head->first, old_entry, new_first)) != old_entry);
+}
+EXPORT_SYMBOL_GPL(llist_add_batch);
+
+/**
+ * llist_del_first - delete the first entry of lock-less list
+ * @head:      the head for your lock-less list
+ *
+ * If list is empty, return NULL, otherwise, return the first entry
+ * deleted, this is the newest added one.
+ *
+ * Only one llist_del_first user can be used simultaneously with
+ * multiple llist_add users without lock.  Because otherwise
+ * llist_del_first, llist_add, llist_add (or llist_del_all, llist_add,
+ * llist_add) sequence in another user may change @head->first->next,
+ * but keep @head->first.  If multiple consumers are needed, please
+ * use llist_del_all or use lock between consumers.
+ */
+struct llist_node *llist_del_first(struct llist_head *head)
+{
+       struct llist_node *entry, *old_entry, *next;
+
+#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+       BUG_ON(in_nmi());
+#endif
+
+       entry = head->first;
+       do {
+               if (entry == NULL)
+                       return NULL;
+               old_entry = entry;
+               next = entry->next;
+               cpu_relax();
+       } while ((entry = cmpxchg(&head->first, old_entry, next)) != old_entry);
+
+       return entry;
+}
+EXPORT_SYMBOL_GPL(llist_del_first);
+
+/**
+ * llist_del_all - delete all entries from lock-less list
+ * @head:      the head of lock-less list to delete all entries
+ *
+ * If list is empty, return NULL, otherwise, delete all entries and
+ * return the pointer to the first entry.  The order of entries
+ * deleted is from the newest to the oldest added one.
+ */
+struct llist_node *llist_del_all(struct llist_head *head)
+{
+#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
+       BUG_ON(in_nmi());
+#endif
+
+       return xchg(&head->first, NULL);
+}
+EXPORT_SYMBOL_GPL(llist_del_all);
index 7ea2e033d7153ad23de9dc02c0c0786abc1e11e2..a2f9da59c1970cfab2a55c94f43da8843551ef05 100644 (file)
@@ -823,8 +823,8 @@ unsigned long radix_tree_prev_hole(struct radix_tree_root *root,
 EXPORT_SYMBOL(radix_tree_prev_hole);
 
 static unsigned int
-__lookup(struct radix_tree_node *slot, void ***results, unsigned long index,
-       unsigned int max_items, unsigned long *next_index)
+__lookup(struct radix_tree_node *slot, void ***results, unsigned long *indices,
+       unsigned long index, unsigned int max_items, unsigned long *next_index)
 {
        unsigned int nr_found = 0;
        unsigned int shift, height;
@@ -857,12 +857,16 @@ __lookup(struct radix_tree_node *slot, void ***results, unsigned long index,
 
        /* Bottom level: grab some items */
        for (i = index & RADIX_TREE_MAP_MASK; i < RADIX_TREE_MAP_SIZE; i++) {
-               index++;
                if (slot->slots[i]) {
-                       results[nr_found++] = &(slot->slots[i]);
-                       if (nr_found == max_items)
+                       results[nr_found] = &(slot->slots[i]);
+                       if (indices)
+                               indices[nr_found] = index;
+                       if (++nr_found == max_items) {
+                               index++;
                                goto out;
+                       }
                }
+               index++;
        }
 out:
        *next_index = index;
@@ -918,8 +922,8 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
 
                if (cur_index > max_index)
                        break;
-               slots_found = __lookup(node, (void ***)results + ret, cur_index,
-                                       max_items - ret, &next_index);
+               slots_found = __lookup(node, (void ***)results + ret, NULL,
+                               cur_index, max_items - ret, &next_index);
                nr_found = 0;
                for (i = 0; i < slots_found; i++) {
                        struct radix_tree_node *slot;
@@ -944,6 +948,7 @@ EXPORT_SYMBOL(radix_tree_gang_lookup);
  *     radix_tree_gang_lookup_slot - perform multiple slot lookup on radix tree
  *     @root:          radix tree root
  *     @results:       where the results of the lookup are placed
+ *     @indices:       where their indices should be placed (but usually NULL)
  *     @first_index:   start the lookup from this key
  *     @max_items:     place up to this many items at *results
  *
@@ -958,7 +963,8 @@ EXPORT_SYMBOL(radix_tree_gang_lookup);
  *     protection, radix_tree_deref_slot may fail requiring a retry.
  */
 unsigned int
-radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results,
+radix_tree_gang_lookup_slot(struct radix_tree_root *root,
+                       void ***results, unsigned long *indices,
                        unsigned long first_index, unsigned int max_items)
 {
        unsigned long max_index;
@@ -974,6 +980,8 @@ radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results,
                if (first_index > 0)
                        return 0;
                results[0] = (void **)&root->rnode;
+               if (indices)
+                       indices[0] = 0;
                return 1;
        }
        node = indirect_to_ptr(node);
@@ -987,8 +995,9 @@ radix_tree_gang_lookup_slot(struct radix_tree_root *root, void ***results,
 
                if (cur_index > max_index)
                        break;
-               slots_found = __lookup(node, results + ret, cur_index,
-                                       max_items - ret, &next_index);
+               slots_found = __lookup(node, results + ret,
+                               indices ? indices + ret : NULL,
+                               cur_index, max_items - ret, &next_index);
                ret += slots_found;
                if (next_index == 0)
                        break;
@@ -1194,6 +1203,98 @@ radix_tree_gang_lookup_tag_slot(struct radix_tree_root *root, void ***results,
 }
 EXPORT_SYMBOL(radix_tree_gang_lookup_tag_slot);
 
+#if defined(CONFIG_SHMEM) && defined(CONFIG_SWAP)
+#include <linux/sched.h> /* for cond_resched() */
+
+/*
+ * This linear search is at present only useful to shmem_unuse_inode().
+ */
+static unsigned long __locate(struct radix_tree_node *slot, void *item,
+                             unsigned long index, unsigned long *found_index)
+{
+       unsigned int shift, height;
+       unsigned long i;
+
+       height = slot->height;
+       shift = (height-1) * RADIX_TREE_MAP_SHIFT;
+
+       for ( ; height > 1; height--) {
+               i = (index >> shift) & RADIX_TREE_MAP_MASK;
+               for (;;) {
+                       if (slot->slots[i] != NULL)
+                               break;
+                       index &= ~((1UL << shift) - 1);
+                       index += 1UL << shift;
+                       if (index == 0)
+                               goto out;       /* 32-bit wraparound */
+                       i++;
+                       if (i == RADIX_TREE_MAP_SIZE)
+                               goto out;
+               }
+
+               shift -= RADIX_TREE_MAP_SHIFT;
+               slot = rcu_dereference_raw(slot->slots[i]);
+               if (slot == NULL)
+                       goto out;
+       }
+
+       /* Bottom level: check items */
+       for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) {
+               if (slot->slots[i] == item) {
+                       *found_index = index + i;
+                       index = 0;
+                       goto out;
+               }
+       }
+       index += RADIX_TREE_MAP_SIZE;
+out:
+       return index;
+}
+
+/**
+ *     radix_tree_locate_item - search through radix tree for item
+ *     @root:          radix tree root
+ *     @item:          item to be found
+ *
+ *     Returns index where item was found, or -1 if not found.
+ *     Caller must hold no lock (since this time-consuming function needs
+ *     to be preemptible), and must check afterwards if item is still there.
+ */
+unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item)
+{
+       struct radix_tree_node *node;
+       unsigned long max_index;
+       unsigned long cur_index = 0;
+       unsigned long found_index = -1;
+
+       do {
+               rcu_read_lock();
+               node = rcu_dereference_raw(root->rnode);
+               if (!radix_tree_is_indirect_ptr(node)) {
+                       rcu_read_unlock();
+                       if (node == item)
+                               found_index = 0;
+                       break;
+               }
+
+               node = indirect_to_ptr(node);
+               max_index = radix_tree_maxindex(node->height);
+               if (cur_index > max_index)
+                       break;
+
+               cur_index = __locate(node, item, cur_index, &found_index);
+               rcu_read_unlock();
+               cond_resched();
+       } while (cur_index != 0 && cur_index <= max_index);
+
+       return found_index;
+}
+#else
+unsigned long radix_tree_locate_item(struct radix_tree_root *root, void *item)
+{
+       return -1;
+}
+#endif /* CONFIG_SHMEM && CONFIG_SWAP */
 
 /**
  *     radix_tree_shrink    -    shrink height of a radix tree to minimal
index 4c45fd50e91367041d825a1146291a829b900eff..f33271dd00cbc7ee39637a1edff2975358daf567 100644 (file)
@@ -1,31 +1,72 @@
 /*
- * SHA transform algorithm, originally taken from code written by
- * Peter Gutmann, and placed in the public domain.
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was based on the git SHA1 implementation.
  */
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/cryptohash.h>
+#include <linux/bitops.h>
+#include <asm/unaligned.h>
 
-/* The SHA f()-functions.  */
+/*
+ * If you have 32 registers or more, the compiler can (and should)
+ * try to change the array[] accesses into registers. However, on
+ * machines with less than ~25 registers, that won't really work,
+ * and at least gcc will make an unholy mess of it.
+ *
+ * So to avoid that mess which just slows things down, we force
+ * the stores to memory to actually happen (we might be better off
+ * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as
+ * suggested by Artur Skawina - that will also make gcc unable to
+ * try to do the silly "optimize away loads" part because it won't
+ * see what the value will be).
+ *
+ * Ben Herrenschmidt reports that on PPC, the C version comes close
+ * to the optimized asm with this (ie on PPC you don't want that
+ * 'volatile', since there are lots of registers).
+ *
+ * On ARM we get the best code generation by forcing a full memory barrier
+ * between each SHA_ROUND, otherwise gcc happily get wild with spilling and
+ * the stack frame size simply explode and performance goes down the drain.
+ */
 
-#define f1(x,y,z)   (z ^ (x & (y ^ z)))                /* x ? y : z */
-#define f2(x,y,z)   (x ^ y ^ z)                        /* XOR */
-#define f3(x,y,z)   ((x & y) + (z & (x ^ y)))  /* majority */
+#ifdef CONFIG_X86
+  #define setW(x, val) (*(volatile __u32 *)&W(x) = (val))
+#elif defined(CONFIG_ARM)
+  #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
+#else
+  #define setW(x, val) (W(x) = (val))
+#endif
 
-/* The SHA Mysterious Constants */
+/* This "rolls" over the 512-bit array */
+#define W(x) (array[(x)&15])
 
-#define K1  0x5A827999L                        /* Rounds  0-19: sqrt(2) * 2^30 */
-#define K2  0x6ED9EBA1L                        /* Rounds 20-39: sqrt(3) * 2^30 */
-#define K3  0x8F1BBCDCL                        /* Rounds 40-59: sqrt(5) * 2^30 */
-#define K4  0xCA62C1D6L                        /* Rounds 60-79: sqrt(10) * 2^30 */
+/*
+ * Where do we get the source from? The first 16 iterations get it from
+ * the input data, the next mix it from the 512-bit array.
+ */
+#define SHA_SRC(t) get_unaligned_be32((__u32 *)data + t)
+#define SHA_MIX(t) rol32(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)
+
+#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \
+       __u32 TEMP = input(t); setW(t, TEMP); \
+       E += TEMP + rol32(A,5) + (fn) + (constant); \
+       B = ror32(B, 2); } while (0)
+
+#define T_0_15(t, A, B, C, D, E)  SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E )
+#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E )
+#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) ,  0xca62c1d6, A, B, C, D, E )
 
 /**
  * sha_transform - single block SHA1 transform
  *
  * @digest: 160 bit digest to update
  * @data:   512 bits of data to hash
- * @W:      80 words of workspace (see note)
+ * @array:  16 words of workspace (see note)
  *
  * This function generates a SHA1 digest for a single 512-bit block.
  * Be warned, it does not handle padding and message digest, do not
  * to clear the workspace. This is left to the caller to avoid
  * unnecessary clears between chained hashing operations.
  */
-void sha_transform(__u32 *digest, const char *in, __u32 *W)
+void sha_transform(__u32 *digest, const char *data, __u32 *array)
 {
-       __u32 a, b, c, d, e, t, i;
-
-       for (i = 0; i < 16; i++)
-               W[i] = be32_to_cpu(((const __be32 *)in)[i]);
-
-       for (i = 0; i < 64; i++)
-               W[i+16] = rol32(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 1);
-
-       a = digest[0];
-       b = digest[1];
-       c = digest[2];
-       d = digest[3];
-       e = digest[4];
-
-       for (i = 0; i < 20; i++) {
-               t = f1(b, c, d) + K1 + rol32(a, 5) + e + W[i];
-               e = d; d = c; c = rol32(b, 30); b = a; a = t;
-       }
-
-       for (; i < 40; i ++) {
-               t = f2(b, c, d) + K2 + rol32(a, 5) + e + W[i];
-               e = d; d = c; c = rol32(b, 30); b = a; a = t;
-       }
-
-       for (; i < 60; i ++) {
-               t = f3(b, c, d) + K3 + rol32(a, 5) + e + W[i];
-               e = d; d = c; c = rol32(b, 30); b = a; a = t;
-       }
-
-       for (; i < 80; i ++) {
-               t = f2(b, c, d) + K4 + rol32(a, 5) + e + W[i];
-               e = d; d = c; c = rol32(b, 30); b = a; a = t;
-       }
-
-       digest[0] += a;
-       digest[1] += b;
-       digest[2] += c;
-       digest[3] += d;
-       digest[4] += e;
+       __u32 A, B, C, D, E;
+
+       A = digest[0];
+       B = digest[1];
+       C = digest[2];
+       D = digest[3];
+       E = digest[4];
+
+       /* Round 1 - iterations 0-16 take their input from 'data' */
+       T_0_15( 0, A, B, C, D, E);
+       T_0_15( 1, E, A, B, C, D);
+       T_0_15( 2, D, E, A, B, C);
+       T_0_15( 3, C, D, E, A, B);
+       T_0_15( 4, B, C, D, E, A);
+       T_0_15( 5, A, B, C, D, E);
+       T_0_15( 6, E, A, B, C, D);
+       T_0_15( 7, D, E, A, B, C);
+       T_0_15( 8, C, D, E, A, B);
+       T_0_15( 9, B, C, D, E, A);
+       T_0_15(10, A, B, C, D, E);
+       T_0_15(11, E, A, B, C, D);
+       T_0_15(12, D, E, A, B, C);
+       T_0_15(13, C, D, E, A, B);
+       T_0_15(14, B, C, D, E, A);
+       T_0_15(15, A, B, C, D, E);
+
+       /* Round 1 - tail. Input from 512-bit mixing array */
+       T_16_19(16, E, A, B, C, D);
+       T_16_19(17, D, E, A, B, C);
+       T_16_19(18, C, D, E, A, B);
+       T_16_19(19, B, C, D, E, A);
+
+       /* Round 2 */
+       T_20_39(20, A, B, C, D, E);
+       T_20_39(21, E, A, B, C, D);
+       T_20_39(22, D, E, A, B, C);
+       T_20_39(23, C, D, E, A, B);
+       T_20_39(24, B, C, D, E, A);
+       T_20_39(25, A, B, C, D, E);
+       T_20_39(26, E, A, B, C, D);
+       T_20_39(27, D, E, A, B, C);
+       T_20_39(28, C, D, E, A, B);
+       T_20_39(29, B, C, D, E, A);
+       T_20_39(30, A, B, C, D, E);
+       T_20_39(31, E, A, B, C, D);
+       T_20_39(32, D, E, A, B, C);
+       T_20_39(33, C, D, E, A, B);
+       T_20_39(34, B, C, D, E, A);
+       T_20_39(35, A, B, C, D, E);
+       T_20_39(36, E, A, B, C, D);
+       T_20_39(37, D, E, A, B, C);
+       T_20_39(38, C, D, E, A, B);
+       T_20_39(39, B, C, D, E, A);
+
+       /* Round 3 */
+       T_40_59(40, A, B, C, D, E);
+       T_40_59(41, E, A, B, C, D);
+       T_40_59(42, D, E, A, B, C);
+       T_40_59(43, C, D, E, A, B);
+       T_40_59(44, B, C, D, E, A);
+       T_40_59(45, A, B, C, D, E);
+       T_40_59(46, E, A, B, C, D);
+       T_40_59(47, D, E, A, B, C);
+       T_40_59(48, C, D, E, A, B);
+       T_40_59(49, B, C, D, E, A);
+       T_40_59(50, A, B, C, D, E);
+       T_40_59(51, E, A, B, C, D);
+       T_40_59(52, D, E, A, B, C);
+       T_40_59(53, C, D, E, A, B);
+       T_40_59(54, B, C, D, E, A);
+       T_40_59(55, A, B, C, D, E);
+       T_40_59(56, E, A, B, C, D);
+       T_40_59(57, D, E, A, B, C);
+       T_40_59(58, C, D, E, A, B);
+       T_40_59(59, B, C, D, E, A);
+
+       /* Round 4 */
+       T_60_79(60, A, B, C, D, E);
+       T_60_79(61, E, A, B, C, D);
+       T_60_79(62, D, E, A, B, C);
+       T_60_79(63, C, D, E, A, B);
+       T_60_79(64, B, C, D, E, A);
+       T_60_79(65, A, B, C, D, E);
+       T_60_79(66, E, A, B, C, D);
+       T_60_79(67, D, E, A, B, C);
+       T_60_79(68, C, D, E, A, B);
+       T_60_79(69, B, C, D, E, A);
+       T_60_79(70, A, B, C, D, E);
+       T_60_79(71, E, A, B, C, D);
+       T_60_79(72, D, E, A, B, C);
+       T_60_79(73, C, D, E, A, B);
+       T_60_79(74, B, C, D, E, A);
+       T_60_79(75, A, B, C, D, E);
+       T_60_79(76, E, A, B, C, D);
+       T_60_79(77, D, E, A, B, C);
+       T_60_79(78, C, D, E, A, B);
+       T_60_79(79, B, C, D, E, A);
+
+       digest[0] += A;
+       digest[1] += B;
+       digest[2] += C;
+       digest[3] += D;
+       digest[4] += E;
 }
 EXPORT_SYMBOL(sha_transform);
 
@@ -92,4 +197,3 @@ void sha_init(__u32 *buf)
        buf[3] = 0x10325476;
        buf[4] = 0xc3d2e1f0;
 }
-
index 1ce58c201dca5c117456ab9d54d4dc48e7809287..0dd7b8fec71cfd6a58c6e42cacf548f5ee2cdf87 100644 (file)
@@ -34,23 +34,23 @@ __setup("failslab=", setup_failslab);
 #ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
 static int __init failslab_debugfs_init(void)
 {
+       struct dentry *dir;
        mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
-       int err;
 
-       err = init_fault_attr_dentries(&failslab.attr, "failslab");
-       if (err)
-               return err;
+       dir = fault_create_debugfs_attr("failslab", NULL, &failslab.attr);
+       if (IS_ERR(dir))
+               return PTR_ERR(dir);
 
-       if (!debugfs_create_bool("ignore-gfp-wait", mode, failslab.attr.dir,
+       if (!debugfs_create_bool("ignore-gfp-wait", mode, dir,
                                &failslab.ignore_gfp_wait))
                goto fail;
-       if (!debugfs_create_bool("cache-filter", mode, failslab.attr.dir,
+       if (!debugfs_create_bool("cache-filter", mode, dir,
                                &failslab.cache_filter))
                goto fail;
 
        return 0;
 fail:
-       cleanup_fault_attr_dentries(&failslab.attr);
+       debugfs_remove_recursive(dir);
 
        return -ENOMEM;
 }
index 867d40222ec798ce99a8332b991e8d7c4bdfb8ec..645a080ba4dfb3be15ba5ab03983f42715f637f9 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/cpuset.h>
 #include <linux/hardirq.h> /* for BUG_ON(!in_atomic()) only */
 #include <linux/memcontrol.h>
-#include <linux/mm_inline.h> /* for page_is_file_cache() */
 #include <linux/cleancache.h>
 #include "internal.h"
 
@@ -462,6 +461,7 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
        int error;
 
        VM_BUG_ON(!PageLocked(page));
+       VM_BUG_ON(PageSwapBacked(page));
 
        error = mem_cgroup_cache_charge(page, current->mm,
                                        gfp_mask & GFP_RECLAIM_MASK);
@@ -479,8 +479,6 @@ int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
                if (likely(!error)) {
                        mapping->nrpages++;
                        __inc_zone_page_state(page, NR_FILE_PAGES);
-                       if (PageSwapBacked(page))
-                               __inc_zone_page_state(page, NR_SHMEM);
                        spin_unlock_irq(&mapping->tree_lock);
                } else {
                        page->mapping = NULL;
@@ -502,22 +500,9 @@ int add_to_page_cache_lru(struct page *page, struct address_space *mapping,
 {
        int ret;
 
-       /*
-        * Splice_read and readahead add shmem/tmpfs pages into the page cache
-        * before shmem_readpage has a chance to mark them as SwapBacked: they
-        * need to go on the anon lru below, and mem_cgroup_cache_charge
-        * (called in add_to_page_cache) needs to know where they're going too.
-        */
-       if (mapping_cap_swap_backed(mapping))
-               SetPageSwapBacked(page);
-
        ret = add_to_page_cache(page, mapping, offset, gfp_mask);
-       if (ret == 0) {
-               if (page_is_file_cache(page))
-                       lru_cache_add_file(page);
-               else
-                       lru_cache_add_anon(page);
-       }
+       if (ret == 0)
+               lru_cache_add_file(page);
        return ret;
 }
 EXPORT_SYMBOL_GPL(add_to_page_cache_lru);
@@ -714,9 +699,16 @@ repeat:
                page = radix_tree_deref_slot(pagep);
                if (unlikely(!page))
                        goto out;
-               if (radix_tree_deref_retry(page))
-                       goto repeat;
-
+               if (radix_tree_exception(page)) {
+                       if (radix_tree_deref_retry(page))
+                               goto repeat;
+                       /*
+                        * Otherwise, shmem/tmpfs must be storing a swap entry
+                        * here as an exceptional entry: so return it without
+                        * attempting to raise page count.
+                        */
+                       goto out;
+               }
                if (!page_cache_get_speculative(page))
                        goto repeat;
 
@@ -753,7 +745,7 @@ struct page *find_lock_page(struct address_space *mapping, pgoff_t offset)
 
 repeat:
        page = find_get_page(mapping, offset);
-       if (page) {
+       if (page && !radix_tree_exception(page)) {
                lock_page(page);
                /* Has the page been truncated? */
                if (unlikely(page->mapping != mapping)) {
@@ -840,7 +832,7 @@ unsigned find_get_pages(struct address_space *mapping, pgoff_t start,
        rcu_read_lock();
 restart:
        nr_found = radix_tree_gang_lookup_slot(&mapping->page_tree,
-                               (void ***)pages, start, nr_pages);
+                               (void ***)pages, NULL, start, nr_pages);
        ret = 0;
        for (i = 0; i < nr_found; i++) {
                struct page *page;
@@ -849,13 +841,22 @@ repeat:
                if (unlikely(!page))
                        continue;
 
-               /*
-                * This can only trigger when the entry at index 0 moves out
-                * of or back to the root: none yet gotten, safe to restart.
-                */
-               if (radix_tree_deref_retry(page)) {
-                       WARN_ON(start | i);
-                       goto restart;
+               if (radix_tree_exception(page)) {
+                       if (radix_tree_deref_retry(page)) {
+                               /*
+                                * Transient condition which can only trigger
+                                * when entry at index 0 moves out of or back
+                                * to root: none yet gotten, safe to restart.
+                                */
+                               WARN_ON(start | i);
+                               goto restart;
+                       }
+                       /*
+                        * Otherwise, shmem/tmpfs must be storing a swap entry
+                        * here as an exceptional entry: so skip over it -
+                        * we only reach this from invalidate_mapping_pages().
+                        */
+                       continue;
                }
 
                if (!page_cache_get_speculative(page))
@@ -903,7 +904,7 @@ unsigned find_get_pages_contig(struct address_space *mapping, pgoff_t index,
        rcu_read_lock();
 restart:
        nr_found = radix_tree_gang_lookup_slot(&mapping->page_tree,
-                               (void ***)pages, index, nr_pages);
+                               (void ***)pages, NULL, index, nr_pages);
        ret = 0;
        for (i = 0; i < nr_found; i++) {
                struct page *page;
@@ -912,12 +913,22 @@ repeat:
                if (unlikely(!page))
                        continue;
 
-               /*
-                * This can only trigger when the entry at index 0 moves out
-                * of or back to the root: none yet gotten, safe to restart.
-                */
-               if (radix_tree_deref_retry(page))
-                       goto restart;
+               if (radix_tree_exception(page)) {
+                       if (radix_tree_deref_retry(page)) {
+                               /*
+                                * Transient condition which can only trigger
+                                * when entry at index 0 moves out of or back
+                                * to root: none yet gotten, safe to restart.
+                                */
+                               goto restart;
+                       }
+                       /*
+                        * Otherwise, shmem/tmpfs must be storing a swap entry
+                        * here as an exceptional entry: so stop looking for
+                        * contiguous pages.
+                        */
+                       break;
+               }
 
                if (!page_cache_get_speculative(page))
                        goto repeat;
@@ -977,12 +988,21 @@ repeat:
                if (unlikely(!page))
                        continue;
 
-               /*
-                * This can only trigger when the entry at index 0 moves out
-                * of or back to the root: none yet gotten, safe to restart.
-                */
-               if (radix_tree_deref_retry(page))
-                       goto restart;
+               if (radix_tree_exception(page)) {
+                       if (radix_tree_deref_retry(page)) {
+                               /*
+                                * Transient condition which can only trigger
+                                * when entry at index 0 moves out of or back
+                                * to root: none yet gotten, safe to restart.
+                                */
+                               goto restart;
+                       }
+                       /*
+                        * This function is never used on a shmem/tmpfs
+                        * mapping, so a swap entry won't be found here.
+                        */
+                       BUG();
+               }
 
                if (!page_cache_get_speculative(page))
                        goto repeat;
index 5f84d2351ddbe942706ed11a53c0574b71724627..f4ec4e7ca4cd2b64f9cfb2ea8845eba90373e549 100644 (file)
@@ -35,7 +35,6 @@
 #include <linux/limits.h>
 #include <linux/mutex.h>
 #include <linux/rbtree.h>
-#include <linux/shmem_fs.h>
 #include <linux/slab.h>
 #include <linux/swap.h>
 #include <linux/swapops.h>
@@ -2873,30 +2872,6 @@ int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
                return 0;
        if (PageCompound(page))
                return 0;
-       /*
-        * Corner case handling. This is called from add_to_page_cache()
-        * in usual. But some FS (shmem) precharges this page before calling it
-        * and call add_to_page_cache() with GFP_NOWAIT.
-        *
-        * For GFP_NOWAIT case, the page may be pre-charged before calling
-        * add_to_page_cache(). (See shmem.c) check it here and avoid to call
-        * charge twice. (It works but has to pay a bit larger cost.)
-        * And when the page is SwapCache, it should take swap information
-        * into account. This is under lock_page() now.
-        */
-       if (!(gfp_mask & __GFP_WAIT)) {
-               struct page_cgroup *pc;
-
-               pc = lookup_page_cgroup(page);
-               if (!pc)
-                       return 0;
-               lock_page_cgroup(pc);
-               if (PageCgroupUsed(pc)) {
-                       unlock_page_cgroup(pc);
-                       return 0;
-               }
-               unlock_page_cgroup(pc);
-       }
 
        if (unlikely(!mm))
                mm = &init_mm;
@@ -3486,31 +3461,6 @@ void mem_cgroup_end_migration(struct mem_cgroup *mem,
        cgroup_release_and_wakeup_rmdir(&mem->css);
 }
 
-/*
- * A call to try to shrink memory usage on charge failure at shmem's swapin.
- * Calling hierarchical_reclaim is not enough because we should update
- * last_oom_jiffies to prevent pagefault_out_of_memory from invoking global OOM.
- * Moreover considering hierarchy, we should reclaim from the mem_over_limit,
- * not from the memcg which this page would be charged to.
- * try_charge_swapin does all of these works properly.
- */
-int mem_cgroup_shmem_charge_fallback(struct page *page,
-                           struct mm_struct *mm,
-                           gfp_t gfp_mask)
-{
-       struct mem_cgroup *mem;
-       int ret;
-
-       if (mem_cgroup_disabled())
-               return 0;
-
-       ret = mem_cgroup_try_charge_swapin(mm, page, gfp_mask, &mem);
-       if (!ret)
-               mem_cgroup_cancel_charge_swapin(mem); /* it does !mem check */
-
-       return ret;
-}
-
 #ifdef CONFIG_DEBUG_VM
 static struct page_cgroup *lookup_page_cgroup_used(struct page *page)
 {
@@ -5330,15 +5280,17 @@ static struct page *mc_handle_file_pte(struct vm_area_struct *vma,
                pgoff = pte_to_pgoff(ptent);
 
        /* page is moved even if it's not RSS of this task(page-faulted). */
-       if (!mapping_cap_swap_backed(mapping)) { /* normal file */
-               page = find_get_page(mapping, pgoff);
-       } else { /* shmem/tmpfs file. we should take account of swap too. */
-               swp_entry_t ent;
-               mem_cgroup_get_shmem_target(inode, pgoff, &page, &ent);
+       page = find_get_page(mapping, pgoff);
+
+#ifdef CONFIG_SWAP
+       /* shmem/tmpfs may report page out on swap: account for that too. */
+       if (radix_tree_exceptional_entry(page)) {
+               swp_entry_t swap = radix_to_swp_entry(page);
                if (do_swap_account)
-                       entry->val = ent.val;
+                       *entry = swap;
+               page = find_get_page(&swapper_space, swap.val);
        }
-
+#endif
        return page;
 }
 
index 740c4f52059cef1bff55fb4293ec41218967fc02..2b43ba051ac94f0512744707fe197d27a6d562b1 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/hugetlb.h>
 #include <linux/memory_hotplug.h>
 #include <linux/mm_inline.h>
+#include <linux/kfifo.h>
 #include "internal.h"
 
 int sysctl_memory_failure_early_kill __read_mostly = 0;
@@ -1178,6 +1179,97 @@ void memory_failure(unsigned long pfn, int trapno)
        __memory_failure(pfn, trapno, 0);
 }
 
+#define MEMORY_FAILURE_FIFO_ORDER      4
+#define MEMORY_FAILURE_FIFO_SIZE       (1 << MEMORY_FAILURE_FIFO_ORDER)
+
+struct memory_failure_entry {
+       unsigned long pfn;
+       int trapno;
+       int flags;
+};
+
+struct memory_failure_cpu {
+       DECLARE_KFIFO(fifo, struct memory_failure_entry,
+                     MEMORY_FAILURE_FIFO_SIZE);
+       spinlock_t lock;
+       struct work_struct work;
+};
+
+static DEFINE_PER_CPU(struct memory_failure_cpu, memory_failure_cpu);
+
+/**
+ * memory_failure_queue - Schedule handling memory failure of a page.
+ * @pfn: Page Number of the corrupted page
+ * @trapno: Trap number reported in the signal to user space.
+ * @flags: Flags for memory failure handling
+ *
+ * This function is called by the low level hardware error handler
+ * when it detects hardware memory corruption of a page. It schedules
+ * the recovering of error page, including dropping pages, killing
+ * processes etc.
+ *
+ * The function is primarily of use for corruptions that
+ * happen outside the current execution context (e.g. when
+ * detected by a background scrubber)
+ *
+ * Can run in IRQ context.
+ */
+void memory_failure_queue(unsigned long pfn, int trapno, int flags)
+{
+       struct memory_failure_cpu *mf_cpu;
+       unsigned long proc_flags;
+       struct memory_failure_entry entry = {
+               .pfn =          pfn,
+               .trapno =       trapno,
+               .flags =        flags,
+       };
+
+       mf_cpu = &get_cpu_var(memory_failure_cpu);
+       spin_lock_irqsave(&mf_cpu->lock, proc_flags);
+       if (kfifo_put(&mf_cpu->fifo, &entry))
+               schedule_work_on(smp_processor_id(), &mf_cpu->work);
+       else
+               pr_err("Memory failure: buffer overflow when queuing memory failure at 0x%#lx\n",
+                      pfn);
+       spin_unlock_irqrestore(&mf_cpu->lock, proc_flags);
+       put_cpu_var(memory_failure_cpu);
+}
+EXPORT_SYMBOL_GPL(memory_failure_queue);
+
+static void memory_failure_work_func(struct work_struct *work)
+{
+       struct memory_failure_cpu *mf_cpu;
+       struct memory_failure_entry entry = { 0, };
+       unsigned long proc_flags;
+       int gotten;
+
+       mf_cpu = &__get_cpu_var(memory_failure_cpu);
+       for (;;) {
+               spin_lock_irqsave(&mf_cpu->lock, proc_flags);
+               gotten = kfifo_get(&mf_cpu->fifo, &entry);
+               spin_unlock_irqrestore(&mf_cpu->lock, proc_flags);
+               if (!gotten)
+                       break;
+               __memory_failure(entry.pfn, entry.trapno, entry.flags);
+       }
+}
+
+static int __init memory_failure_init(void)
+{
+       struct memory_failure_cpu *mf_cpu;
+       int cpu;
+
+       for_each_possible_cpu(cpu) {
+               mf_cpu = &per_cpu(memory_failure_cpu, cpu);
+               spin_lock_init(&mf_cpu->lock);
+               INIT_KFIFO(mf_cpu->fifo);
+               INIT_WORK(&mf_cpu->work, memory_failure_work_func);
+       }
+
+       return 0;
+}
+core_initcall(memory_failure_init);
+
 /**
  * unpoison_memory - Unpoison a previously poisoned page
  * @pfn: Page number of the to be unpoisoned page
index a4e6b9d75c76198be4f04d41e996f2067a97db27..636a86876ff217a7f6d18f29c18524c2f2645a63 100644 (file)
@@ -69,12 +69,15 @@ static unsigned char mincore_page(struct address_space *mapping, pgoff_t pgoff)
         * file will not get a swp_entry_t in its pte, but rather it is like
         * any other file mapping (ie. marked !present and faulted in with
         * tmpfs's .fault). So swapped out tmpfs mappings are tested here.
-        *
-        * However when tmpfs moves the page from pagecache and into swapcache,
-        * it is still in core, but the find_get_page below won't find it.
-        * No big deal, but make a note of it.
         */
        page = find_get_page(mapping, pgoff);
+#ifdef CONFIG_SWAP
+       /* shmem/tmpfs may return swap: account for swapcache page too. */
+       if (radix_tree_exceptional_entry(page)) {
+               swp_entry_t swap = radix_to_swp_entry(page);
+               page = find_get_page(&swapper_space, swap.val);
+       }
+#endif
        if (page) {
                present = PageUptodate(page);
                page_cache_release(page);
index eafff89b3dd634e8836842005695c1ad48cc0cb2..626303b52f3ce0764d3bb1029f6bce8b5bcaa896 100644 (file)
@@ -303,7 +303,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
        do_each_thread(g, p) {
                unsigned int points;
 
-               if (!p->mm)
+               if (p->exit_state)
                        continue;
                if (oom_unkillable_task(p, mem, nodemask))
                        continue;
@@ -319,6 +319,8 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
                 */
                if (test_tsk_thread_flag(p, TIF_MEMDIE))
                        return ERR_PTR(-1UL);
+               if (!p->mm)
+                       continue;
 
                if (p->flags & PF_EXITING) {
                        /*
index 1dbcf8888f14564612944ed877008a859b394857..6e8ecb6e021c7ebc4056a2eb23576c314721b858 100644 (file)
@@ -1409,14 +1409,11 @@ static int __init fail_page_alloc_debugfs(void)
 {
        mode_t mode = S_IFREG | S_IRUSR | S_IWUSR;
        struct dentry *dir;
-       int err;
 
-       err = init_fault_attr_dentries(&fail_page_alloc.attr,
-                                      "fail_page_alloc");
-       if (err)
-               return err;
-
-       dir = fail_page_alloc.attr.dir;
+       dir = fault_create_debugfs_attr("fail_page_alloc", NULL,
+                                       &fail_page_alloc.attr);
+       if (IS_ERR(dir))
+               return PTR_ERR(dir);
 
        if (!debugfs_create_bool("ignore-gfp-wait", mode, dir,
                                &fail_page_alloc.ignore_gfp_wait))
@@ -1430,7 +1427,7 @@ static int __init fail_page_alloc_debugfs(void)
 
        return 0;
 fail:
-       cleanup_fault_attr_dentries(&fail_page_alloc.attr);
+       debugfs_remove_recursive(dir);
 
        return -ENOMEM;
 }
index 5cc21f8b4cd3e7e39dcd2f20f4fd0f710cf47dee..32f6763f16fb82ad7068c4d73479a96583f4ef15 100644 (file)
@@ -6,7 +6,8 @@
  *              2000-2001 Christoph Rohland
  *              2000-2001 SAP AG
  *              2002 Red Hat Inc.
- * Copyright (C) 2002-2005 Hugh Dickins.
+ * Copyright (C) 2002-2011 Hugh Dickins.
+ * Copyright (C) 2011 Google Inc.
  * Copyright (C) 2002-2005 VERITAS Software Corporation.
  * Copyright (C) 2004 Andi Kleen, SuSE Labs
  *
@@ -28,7 +29,6 @@
 #include <linux/file.h>
 #include <linux/mm.h>
 #include <linux/module.h>
-#include <linux/percpu_counter.h>
 #include <linux/swap.h>
 
 static struct vfsmount *shm_mnt;
@@ -51,6 +51,8 @@ static struct vfsmount *shm_mnt;
 #include <linux/shmem_fs.h>
 #include <linux/writeback.h>
 #include <linux/blkdev.h>
+#include <linux/pagevec.h>
+#include <linux/percpu_counter.h>
 #include <linux/splice.h>
 #include <linux/security.h>
 #include <linux/swapops.h>
@@ -63,43 +65,17 @@ static struct vfsmount *shm_mnt;
 #include <linux/magic.h>
 
 #include <asm/uaccess.h>
-#include <asm/div64.h>
 #include <asm/pgtable.h>
 
-/*
- * The maximum size of a shmem/tmpfs file is limited by the maximum size of
- * its triple-indirect swap vector - see illustration at shmem_swp_entry().
- *
- * With 4kB page size, maximum file size is just over 2TB on a 32-bit kernel,
- * but one eighth of that on a 64-bit kernel.  With 8kB page size, maximum
- * file size is just over 4TB on a 64-bit kernel, but 16TB on a 32-bit kernel,
- * MAX_LFS_FILESIZE being then more restrictive than swap vector layout.
- *
- * We use / and * instead of shifts in the definitions below, so that the swap
- * vector can be tested with small even values (e.g. 20) for ENTRIES_PER_PAGE.
- */
-#define ENTRIES_PER_PAGE (PAGE_CACHE_SIZE/sizeof(unsigned long))
-#define ENTRIES_PER_PAGEPAGE ((unsigned long long)ENTRIES_PER_PAGE*ENTRIES_PER_PAGE)
-
-#define SHMSWP_MAX_INDEX (SHMEM_NR_DIRECT + (ENTRIES_PER_PAGEPAGE/2) * (ENTRIES_PER_PAGE+1))
-#define SHMSWP_MAX_BYTES (SHMSWP_MAX_INDEX << PAGE_CACHE_SHIFT)
-
-#define SHMEM_MAX_BYTES  min_t(unsigned long long, SHMSWP_MAX_BYTES, MAX_LFS_FILESIZE)
-#define SHMEM_MAX_INDEX  ((unsigned long)((SHMEM_MAX_BYTES+1) >> PAGE_CACHE_SHIFT))
-
 #define BLOCKS_PER_PAGE  (PAGE_CACHE_SIZE/512)
 #define VM_ACCT(size)    (PAGE_CACHE_ALIGN(size) >> PAGE_SHIFT)
 
-/* info->flags needs VM_flags to handle pagein/truncate races efficiently */
-#define SHMEM_PAGEIN    VM_READ
-#define SHMEM_TRUNCATE  VM_WRITE
-
-/* Definition to limit shmem_truncate's steps between cond_rescheds */
-#define LATENCY_LIMIT   64
-
 /* Pretend that each entry is of this size in directory's i_size */
 #define BOGO_DIRENT_SIZE 20
 
+/* Symlink up to this size is kmalloc'ed instead of using a swappable page */
+#define SHORT_SYMLINK_LEN 128
+
 struct shmem_xattr {
        struct list_head list;  /* anchored by shmem_inode_info->xattr_list */
        char *name;             /* xattr name */
@@ -107,7 +83,7 @@ struct shmem_xattr {
        char value[0];
 };
 
-/* Flag allocation requirements to shmem_getpage and shmem_swp_alloc */
+/* Flag allocation requirements to shmem_getpage */
 enum sgp_type {
        SGP_READ,       /* don't exceed i_size, don't allocate page */
        SGP_CACHE,      /* don't exceed i_size, may allocate page */
@@ -137,56 +113,6 @@ static inline int shmem_getpage(struct inode *inode, pgoff_t index,
                        mapping_gfp_mask(inode->i_mapping), fault_type);
 }
 
-static inline struct page *shmem_dir_alloc(gfp_t gfp_mask)
-{
-       /*
-        * The above definition of ENTRIES_PER_PAGE, and the use of
-        * BLOCKS_PER_PAGE on indirect pages, assume PAGE_CACHE_SIZE:
-        * might be reconsidered if it ever diverges from PAGE_SIZE.
-        *
-        * Mobility flags are masked out as swap vectors cannot move
-        */
-       return alloc_pages((gfp_mask & ~GFP_MOVABLE_MASK) | __GFP_ZERO,
-                               PAGE_CACHE_SHIFT-PAGE_SHIFT);
-}
-
-static inline void shmem_dir_free(struct page *page)
-{
-       __free_pages(page, PAGE_CACHE_SHIFT-PAGE_SHIFT);
-}
-
-static struct page **shmem_dir_map(struct page *page)
-{
-       return (struct page **)kmap_atomic(page, KM_USER0);
-}
-
-static inline void shmem_dir_unmap(struct page **dir)
-{
-       kunmap_atomic(dir, KM_USER0);
-}
-
-static swp_entry_t *shmem_swp_map(struct page *page)
-{
-       return (swp_entry_t *)kmap_atomic(page, KM_USER1);
-}
-
-static inline void shmem_swp_balance_unmap(void)
-{
-       /*
-        * When passing a pointer to an i_direct entry, to code which
-        * also handles indirect entries and so will shmem_swp_unmap,
-        * we must arrange for the preempt count to remain in balance.
-        * What kmap_atomic of a lowmem page does depends on config
-        * and architecture, so pretend to kmap_atomic some lowmem page.
-        */
-       (void) kmap_atomic(ZERO_PAGE(0), KM_USER1);
-}
-
-static inline void shmem_swp_unmap(swp_entry_t *entry)
-{
-       kunmap_atomic(entry, KM_USER1);
-}
-
 static inline struct shmem_sb_info *SHMEM_SB(struct super_block *sb)
 {
        return sb->s_fs_info;
@@ -244,15 +170,6 @@ static struct backing_dev_info shmem_backing_dev_info  __read_mostly = {
 static LIST_HEAD(shmem_swaplist);
 static DEFINE_MUTEX(shmem_swaplist_mutex);
 
-static void shmem_free_blocks(struct inode *inode, long pages)
-{
-       struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
-       if (sbinfo->max_blocks) {
-               percpu_counter_add(&sbinfo->used_blocks, -pages);
-               inode->i_blocks -= pages*BLOCKS_PER_PAGE;
-       }
-}
-
 static int shmem_reserve_inode(struct super_block *sb)
 {
        struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
@@ -279,7 +196,7 @@ static void shmem_free_inode(struct super_block *sb)
 }
 
 /**
- * shmem_recalc_inode - recalculate the size of an inode
+ * shmem_recalc_inode - recalculate the block usage of an inode
  * @inode: inode to recalc
  *
  * We have to calculate the free blocks since the mm can drop
@@ -297,474 +214,297 @@ static void shmem_recalc_inode(struct inode *inode)
 
        freed = info->alloced - info->swapped - inode->i_mapping->nrpages;
        if (freed > 0) {
+               struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+               if (sbinfo->max_blocks)
+                       percpu_counter_add(&sbinfo->used_blocks, -freed);
                info->alloced -= freed;
+               inode->i_blocks -= freed * BLOCKS_PER_PAGE;
                shmem_unacct_blocks(info->flags, freed);
-               shmem_free_blocks(inode, freed);
        }
 }
 
-/**
- * shmem_swp_entry - find the swap vector position in the info structure
- * @info:  info structure for the inode
- * @index: index of the page to find
- * @page:  optional page to add to the structure. Has to be preset to
- *         all zeros
- *
- * If there is no space allocated yet it will return NULL when
- * page is NULL, else it will use the page for the needed block,
- * setting it to NULL on return to indicate that it has been used.
- *
- * The swap vector is organized the following way:
- *
- * There are SHMEM_NR_DIRECT entries directly stored in the
- * shmem_inode_info structure. So small files do not need an addional
- * allocation.
- *
- * For pages with index > SHMEM_NR_DIRECT there is the pointer
- * i_indirect which points to a page which holds in the first half
- * doubly indirect blocks, in the second half triple indirect blocks:
- *
- * For an artificial ENTRIES_PER_PAGE = 4 this would lead to the
- * following layout (for SHMEM_NR_DIRECT == 16):
- *
- * i_indirect -> dir --> 16-19
- *           |      +-> 20-23
- *           |
- *           +-->dir2 --> 24-27
- *           |        +-> 28-31
- *           |        +-> 32-35
- *           |        +-> 36-39
- *           |
- *           +-->dir3 --> 40-43
- *                    +-> 44-47
- *                    +-> 48-51
- *                    +-> 52-55
+/*
+ * Replace item expected in radix tree by a new item, while holding tree lock.
  */
-static swp_entry_t *shmem_swp_entry(struct shmem_inode_info *info, unsigned long index, struct page **page)
-{
-       unsigned long offset;
-       struct page **dir;
-       struct page *subdir;
-
-       if (index < SHMEM_NR_DIRECT) {
-               shmem_swp_balance_unmap();
-               return info->i_direct+index;
-       }
-       if (!info->i_indirect) {
-               if (page) {
-                       info->i_indirect = *page;
-                       *page = NULL;
-               }
-               return NULL;                    /* need another page */
-       }
-
-       index -= SHMEM_NR_DIRECT;
-       offset = index % ENTRIES_PER_PAGE;
-       index /= ENTRIES_PER_PAGE;
-       dir = shmem_dir_map(info->i_indirect);
-
-       if (index >= ENTRIES_PER_PAGE/2) {
-               index -= ENTRIES_PER_PAGE/2;
-               dir += ENTRIES_PER_PAGE/2 + index/ENTRIES_PER_PAGE;
-               index %= ENTRIES_PER_PAGE;
-               subdir = *dir;
-               if (!subdir) {
-                       if (page) {
-                               *dir = *page;
-                               *page = NULL;
-                       }
-                       shmem_dir_unmap(dir);
-                       return NULL;            /* need another page */
-               }
-               shmem_dir_unmap(dir);
-               dir = shmem_dir_map(subdir);
-       }
+static int shmem_radix_tree_replace(struct address_space *mapping,
+                       pgoff_t index, void *expected, void *replacement)
+{
+       void **pslot;
+       void *item = NULL;
+
+       VM_BUG_ON(!expected);
+       pslot = radix_tree_lookup_slot(&mapping->page_tree, index);
+       if (pslot)
+               item = radix_tree_deref_slot_protected(pslot,
+                                                       &mapping->tree_lock);
+       if (item != expected)
+               return -ENOENT;
+       if (replacement)
+               radix_tree_replace_slot(pslot, replacement);
+       else
+               radix_tree_delete(&mapping->page_tree, index);
+       return 0;
+}
 
-       dir += index;
-       subdir = *dir;
-       if (!subdir) {
-               if (!page || !(subdir = *page)) {
-                       shmem_dir_unmap(dir);
-                       return NULL;            /* need a page */
+/*
+ * Like add_to_page_cache_locked, but error if expected item has gone.
+ */
+static int shmem_add_to_page_cache(struct page *page,
+                                  struct address_space *mapping,
+                                  pgoff_t index, gfp_t gfp, void *expected)
+{
+       int error = 0;
+
+       VM_BUG_ON(!PageLocked(page));
+       VM_BUG_ON(!PageSwapBacked(page));
+
+       if (!expected)
+               error = radix_tree_preload(gfp & GFP_RECLAIM_MASK);
+       if (!error) {
+               page_cache_get(page);
+               page->mapping = mapping;
+               page->index = index;
+
+               spin_lock_irq(&mapping->tree_lock);
+               if (!expected)
+                       error = radix_tree_insert(&mapping->page_tree,
+                                                       index, page);
+               else
+                       error = shmem_radix_tree_replace(mapping, index,
+                                                       expected, page);
+               if (!error) {
+                       mapping->nrpages++;
+                       __inc_zone_page_state(page, NR_FILE_PAGES);
+                       __inc_zone_page_state(page, NR_SHMEM);
+                       spin_unlock_irq(&mapping->tree_lock);
+               } else {
+                       page->mapping = NULL;
+                       spin_unlock_irq(&mapping->tree_lock);
+                       page_cache_release(page);
                }
-               *dir = subdir;
-               *page = NULL;
+               if (!expected)
+                       radix_tree_preload_end();
        }
-       shmem_dir_unmap(dir);
-       return shmem_swp_map(subdir) + offset;
+       if (error)
+               mem_cgroup_uncharge_cache_page(page);
+       return error;
 }
 
-static void shmem_swp_set(struct shmem_inode_info *info, swp_entry_t *entry, unsigned long value)
+/*
+ * Like delete_from_page_cache, but substitutes swap for page.
+ */
+static void shmem_delete_from_page_cache(struct page *page, void *radswap)
 {
-       long incdec = value? 1: -1;
+       struct address_space *mapping = page->mapping;
+       int error;
 
-       entry->val = value;
-       info->swapped += incdec;
-       if ((unsigned long)(entry - info->i_direct) >= SHMEM_NR_DIRECT) {
-               struct page *page = kmap_atomic_to_page(entry);
-               set_page_private(page, page_private(page) + incdec);
-       }
+       spin_lock_irq(&mapping->tree_lock);
+       error = shmem_radix_tree_replace(mapping, page->index, page, radswap);
+       page->mapping = NULL;
+       mapping->nrpages--;
+       __dec_zone_page_state(page, NR_FILE_PAGES);
+       __dec_zone_page_state(page, NR_SHMEM);
+       spin_unlock_irq(&mapping->tree_lock);
+       page_cache_release(page);
+       BUG_ON(error);
 }
 
-/**
- * shmem_swp_alloc - get the position of the swap entry for the page.
- * @info:      info structure for the inode
- * @index:     index of the page to find
- * @sgp:       check and recheck i_size? skip allocation?
- * @gfp:       gfp mask to use for any page allocation
- *
- * If the entry does not exist, allocate it.
+/*
+ * Like find_get_pages, but collecting swap entries as well as pages.
  */
-static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info,
-                       unsigned long index, enum sgp_type sgp, gfp_t gfp)
-{
-       struct inode *inode = &info->vfs_inode;
-       struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
-       struct page *page = NULL;
-       swp_entry_t *entry;
-
-       if (sgp != SGP_WRITE &&
-           ((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode))
-               return ERR_PTR(-EINVAL);
-
-       while (!(entry = shmem_swp_entry(info, index, &page))) {
-               if (sgp == SGP_READ)
-                       return shmem_swp_map(ZERO_PAGE(0));
-               /*
-                * Test used_blocks against 1 less max_blocks, since we have 1 data
-                * page (and perhaps indirect index pages) yet to allocate:
-                * a waste to allocate index if we cannot allocate data.
-                */
-               if (sbinfo->max_blocks) {
-                       if (percpu_counter_compare(&sbinfo->used_blocks,
-                                               sbinfo->max_blocks - 1) >= 0)
-                               return ERR_PTR(-ENOSPC);
-                       percpu_counter_inc(&sbinfo->used_blocks);
-                       inode->i_blocks += BLOCKS_PER_PAGE;
+static unsigned shmem_find_get_pages_and_swap(struct address_space *mapping,
+                                       pgoff_t start, unsigned int nr_pages,
+                                       struct page **pages, pgoff_t *indices)
+{
+       unsigned int i;
+       unsigned int ret;
+       unsigned int nr_found;
+
+       rcu_read_lock();
+restart:
+       nr_found = radix_tree_gang_lookup_slot(&mapping->page_tree,
+                               (void ***)pages, indices, start, nr_pages);
+       ret = 0;
+       for (i = 0; i < nr_found; i++) {
+               struct page *page;
+repeat:
+               page = radix_tree_deref_slot((void **)pages[i]);
+               if (unlikely(!page))
+                       continue;
+               if (radix_tree_exception(page)) {
+                       if (radix_tree_deref_retry(page))
+                               goto restart;
+                       /*
+                        * Otherwise, we must be storing a swap entry
+                        * here as an exceptional entry: so return it
+                        * without attempting to raise page count.
+                        */
+                       goto export;
                }
+               if (!page_cache_get_speculative(page))
+                       goto repeat;
 
-               spin_unlock(&info->lock);
-               page = shmem_dir_alloc(gfp);
-               spin_lock(&info->lock);
-
-               if (!page) {
-                       shmem_free_blocks(inode, 1);
-                       return ERR_PTR(-ENOMEM);
-               }
-               if (sgp != SGP_WRITE &&
-                   ((loff_t) index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
-                       entry = ERR_PTR(-EINVAL);
-                       break;
+               /* Has the page moved? */
+               if (unlikely(page != *((void **)pages[i]))) {
+                       page_cache_release(page);
+                       goto repeat;
                }
-               if (info->next_index <= index)
-                       info->next_index = index + 1;
-       }
-       if (page) {
-               /* another task gave its page, or truncated the file */
-               shmem_free_blocks(inode, 1);
-               shmem_dir_free(page);
-       }
-       if (info->next_index <= index && !IS_ERR(entry))
-               info->next_index = index + 1;
-       return entry;
+export:
+               indices[ret] = indices[i];
+               pages[ret] = page;
+               ret++;
+       }
+       if (unlikely(!ret && nr_found))
+               goto restart;
+       rcu_read_unlock();
+       return ret;
 }
 
-/**
- * shmem_free_swp - free some swap entries in a directory
- * @dir:        pointer to the directory
- * @edir:       pointer after last entry of the directory
- * @punch_lock: pointer to spinlock when needed for the holepunch case
+/*
+ * Remove swap entry from radix tree, free the swap and its page cache.
  */
-static int shmem_free_swp(swp_entry_t *dir, swp_entry_t *edir,
-                                               spinlock_t *punch_lock)
-{
-       spinlock_t *punch_unlock = NULL;
-       swp_entry_t *ptr;
-       int freed = 0;
-
-       for (ptr = dir; ptr < edir; ptr++) {
-               if (ptr->val) {
-                       if (unlikely(punch_lock)) {
-                               punch_unlock = punch_lock;
-                               punch_lock = NULL;
-                               spin_lock(punch_unlock);
-                               if (!ptr->val)
-                                       continue;
-                       }
-                       free_swap_and_cache(*ptr);
-                       *ptr = (swp_entry_t){0};
-                       freed++;
-               }
-       }
-       if (punch_unlock)
-               spin_unlock(punch_unlock);
-       return freed;
-}
-
-static int shmem_map_and_free_swp(struct page *subdir, int offset,
-               int limit, struct page ***dir, spinlock_t *punch_lock)
-{
-       swp_entry_t *ptr;
-       int freed = 0;
-
-       ptr = shmem_swp_map(subdir);
-       for (; offset < limit; offset += LATENCY_LIMIT) {
-               int size = limit - offset;
-               if (size > LATENCY_LIMIT)
-                       size = LATENCY_LIMIT;
-               freed += shmem_free_swp(ptr+offset, ptr+offset+size,
-                                                       punch_lock);
-               if (need_resched()) {
-                       shmem_swp_unmap(ptr);
-                       if (*dir) {
-                               shmem_dir_unmap(*dir);
-                               *dir = NULL;
-                       }
-                       cond_resched();
-                       ptr = shmem_swp_map(subdir);
-               }
-       }
-       shmem_swp_unmap(ptr);
-       return freed;
+static int shmem_free_swap(struct address_space *mapping,
+                          pgoff_t index, void *radswap)
+{
+       int error;
+
+       spin_lock_irq(&mapping->tree_lock);
+       error = shmem_radix_tree_replace(mapping, index, radswap, NULL);
+       spin_unlock_irq(&mapping->tree_lock);
+       if (!error)
+               free_swap_and_cache(radix_to_swp_entry(radswap));
+       return error;
 }
 
-static void shmem_free_pages(struct list_head *next)
+/*
+ * Pagevec may contain swap entries, so shuffle up pages before releasing.
+ */
+static void shmem_pagevec_release(struct pagevec *pvec)
 {
-       struct page *page;
-       int freed = 0;
-
-       do {
-               page = container_of(next, struct page, lru);
-               next = next->next;
-               shmem_dir_free(page);
-               freed++;
-               if (freed >= LATENCY_LIMIT) {
-                       cond_resched();
-                       freed = 0;
-               }
-       } while (next);
+       int i, j;
+
+       for (i = 0, j = 0; i < pagevec_count(pvec); i++) {
+               struct page *page = pvec->pages[i];
+               if (!radix_tree_exceptional_entry(page))
+                       pvec->pages[j++] = page;
+       }
+       pvec->nr = j;
+       pagevec_release(pvec);
 }
 
-void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end)
+/*
+ * Remove range of pages and swap entries from radix tree, and free them.
+ */
+void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
 {
+       struct address_space *mapping = inode->i_mapping;
        struct shmem_inode_info *info = SHMEM_I(inode);
-       unsigned long idx;
-       unsigned long size;
-       unsigned long limit;
-       unsigned long stage;
-       unsigned long diroff;
-       struct page **dir;
-       struct page *topdir;
-       struct page *middir;
-       struct page *subdir;
-       swp_entry_t *ptr;
-       LIST_HEAD(pages_to_free);
-       long nr_pages_to_free = 0;
+       pgoff_t start = (lstart + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       unsigned partial = lstart & (PAGE_CACHE_SIZE - 1);
+       pgoff_t end = (lend >> PAGE_CACHE_SHIFT);
+       struct pagevec pvec;
+       pgoff_t indices[PAGEVEC_SIZE];
        long nr_swaps_freed = 0;
-       int offset;
-       int freed;
-       int punch_hole;
-       spinlock_t *needs_lock;
-       spinlock_t *punch_lock;
-       unsigned long upper_limit;
+       pgoff_t index;
+       int i;
 
-       truncate_inode_pages_range(inode->i_mapping, start, end);
+       BUG_ON((lend & (PAGE_CACHE_SIZE - 1)) != (PAGE_CACHE_SIZE - 1));
 
-       inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-       idx = (start + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-       if (idx >= info->next_index)
-               return;
+       pagevec_init(&pvec, 0);
+       index = start;
+       while (index <= end) {
+               pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
+                       min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1,
+                                                       pvec.pages, indices);
+               if (!pvec.nr)
+                       break;
+               mem_cgroup_uncharge_start();
+               for (i = 0; i < pagevec_count(&pvec); i++) {
+                       struct page *page = pvec.pages[i];
 
-       spin_lock(&info->lock);
-       info->flags |= SHMEM_TRUNCATE;
-       if (likely(end == (loff_t) -1)) {
-               limit = info->next_index;
-               upper_limit = SHMEM_MAX_INDEX;
-               info->next_index = idx;
-               needs_lock = NULL;
-               punch_hole = 0;
-       } else {
-               if (end + 1 >= inode->i_size) { /* we may free a little more */
-                       limit = (inode->i_size + PAGE_CACHE_SIZE - 1) >>
-                                                       PAGE_CACHE_SHIFT;
-                       upper_limit = SHMEM_MAX_INDEX;
-               } else {
-                       limit = (end + 1) >> PAGE_CACHE_SHIFT;
-                       upper_limit = limit;
-               }
-               needs_lock = &info->lock;
-               punch_hole = 1;
-       }
+                       index = indices[i];
+                       if (index > end)
+                               break;
+
+                       if (radix_tree_exceptional_entry(page)) {
+                               nr_swaps_freed += !shmem_free_swap(mapping,
+                                                               index, page);
+                               continue;
+                       }
 
-       topdir = info->i_indirect;
-       if (topdir && idx <= SHMEM_NR_DIRECT && !punch_hole) {
-               info->i_indirect = NULL;
-               nr_pages_to_free++;
-               list_add(&topdir->lru, &pages_to_free);
+                       if (!trylock_page(page))
+                               continue;
+                       if (page->mapping == mapping) {
+                               VM_BUG_ON(PageWriteback(page));
+                               truncate_inode_page(mapping, page);
+                       }
+                       unlock_page(page);
+               }
+               shmem_pagevec_release(&pvec);
+               mem_cgroup_uncharge_end();
+               cond_resched();
+               index++;
        }
-       spin_unlock(&info->lock);
 
-       if (info->swapped && idx < SHMEM_NR_DIRECT) {
-               ptr = info->i_direct;
-               size = limit;
-               if (size > SHMEM_NR_DIRECT)
-                       size = SHMEM_NR_DIRECT;
-               nr_swaps_freed = shmem_free_swp(ptr+idx, ptr+size, needs_lock);
+       if (partial) {
+               struct page *page = NULL;
+               shmem_getpage(inode, start - 1, &page, SGP_READ, NULL);
+               if (page) {
+                       zero_user_segment(page, partial, PAGE_CACHE_SIZE);
+                       set_page_dirty(page);
+                       unlock_page(page);
+                       page_cache_release(page);
+               }
        }
 
-       /*
-        * If there are no indirect blocks or we are punching a hole
-        * below indirect blocks, nothing to be done.
-        */
-       if (!topdir || limit <= SHMEM_NR_DIRECT)
-               goto done2;
+       index = start;
+       for ( ; ; ) {
+               cond_resched();
+               pvec.nr = shmem_find_get_pages_and_swap(mapping, index,
+                       min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1,
+                                                       pvec.pages, indices);
+               if (!pvec.nr) {
+                       if (index == start)
+                               break;
+                       index = start;
+                       continue;
+               }
+               if (index == start && indices[0] > end) {
+                       shmem_pagevec_release(&pvec);
+                       break;
+               }
+               mem_cgroup_uncharge_start();
+               for (i = 0; i < pagevec_count(&pvec); i++) {
+                       struct page *page = pvec.pages[i];
 
-       /*
-        * The truncation case has already dropped info->lock, and we're safe
-        * because i_size and next_index have already been lowered, preventing
-        * access beyond.  But in the punch_hole case, we still need to take
-        * the lock when updating the swap directory, because there might be
-        * racing accesses by shmem_getpage(SGP_CACHE), shmem_unuse_inode or
-        * shmem_writepage.  However, whenever we find we can remove a whole
-        * directory page (not at the misaligned start or end of the range),
-        * we first NULLify its pointer in the level above, and then have no
-        * need to take the lock when updating its contents: needs_lock and
-        * punch_lock (either pointing to info->lock or NULL) manage this.
-        */
+                       index = indices[i];
+                       if (index > end)
+                               break;
 
-       upper_limit -= SHMEM_NR_DIRECT;
-       limit -= SHMEM_NR_DIRECT;
-       idx = (idx > SHMEM_NR_DIRECT)? (idx - SHMEM_NR_DIRECT): 0;
-       offset = idx % ENTRIES_PER_PAGE;
-       idx -= offset;
-
-       dir = shmem_dir_map(topdir);
-       stage = ENTRIES_PER_PAGEPAGE/2;
-       if (idx < ENTRIES_PER_PAGEPAGE/2) {
-               middir = topdir;
-               diroff = idx/ENTRIES_PER_PAGE;
-       } else {
-               dir += ENTRIES_PER_PAGE/2;
-               dir += (idx - ENTRIES_PER_PAGEPAGE/2)/ENTRIES_PER_PAGEPAGE;
-               while (stage <= idx)
-                       stage += ENTRIES_PER_PAGEPAGE;
-               middir = *dir;
-               if (*dir) {
-                       diroff = ((idx - ENTRIES_PER_PAGEPAGE/2) %
-                               ENTRIES_PER_PAGEPAGE) / ENTRIES_PER_PAGE;
-                       if (!diroff && !offset && upper_limit >= stage) {
-                               if (needs_lock) {
-                                       spin_lock(needs_lock);
-                                       *dir = NULL;
-                                       spin_unlock(needs_lock);
-                                       needs_lock = NULL;
-                               } else
-                                       *dir = NULL;
-                               nr_pages_to_free++;
-                               list_add(&middir->lru, &pages_to_free);
+                       if (radix_tree_exceptional_entry(page)) {
+                               nr_swaps_freed += !shmem_free_swap(mapping,
+                                                               index, page);
+                               continue;
                        }
-                       shmem_dir_unmap(dir);
-                       dir = shmem_dir_map(middir);
-               } else {
-                       diroff = 0;
-                       offset = 0;
-                       idx = stage;
-               }
-       }
 
-       for (; idx < limit; idx += ENTRIES_PER_PAGE, diroff++) {
-               if (unlikely(idx == stage)) {
-                       shmem_dir_unmap(dir);
-                       dir = shmem_dir_map(topdir) +
-                           ENTRIES_PER_PAGE/2 + idx/ENTRIES_PER_PAGEPAGE;
-                       while (!*dir) {
-                               dir++;
-                               idx += ENTRIES_PER_PAGEPAGE;
-                               if (idx >= limit)
-                                       goto done1;
-                       }
-                       stage = idx + ENTRIES_PER_PAGEPAGE;
-                       middir = *dir;
-                       if (punch_hole)
-                               needs_lock = &info->lock;
-                       if (upper_limit >= stage) {
-                               if (needs_lock) {
-                                       spin_lock(needs_lock);
-                                       *dir = NULL;
-                                       spin_unlock(needs_lock);
-                                       needs_lock = NULL;
-                               } else
-                                       *dir = NULL;
-                               nr_pages_to_free++;
-                               list_add(&middir->lru, &pages_to_free);
+                       lock_page(page);
+                       if (page->mapping == mapping) {
+                               VM_BUG_ON(PageWriteback(page));
+                               truncate_inode_page(mapping, page);
                        }
-                       shmem_dir_unmap(dir);
-                       cond_resched();
-                       dir = shmem_dir_map(middir);
-                       diroff = 0;
-               }
-               punch_lock = needs_lock;
-               subdir = dir[diroff];
-               if (subdir && !offset && upper_limit-idx >= ENTRIES_PER_PAGE) {
-                       if (needs_lock) {
-                               spin_lock(needs_lock);
-                               dir[diroff] = NULL;
-                               spin_unlock(needs_lock);
-                               punch_lock = NULL;
-                       } else
-                               dir[diroff] = NULL;
-                       nr_pages_to_free++;
-                       list_add(&subdir->lru, &pages_to_free);
-               }
-               if (subdir && page_private(subdir) /* has swap entries */) {
-                       size = limit - idx;
-                       if (size > ENTRIES_PER_PAGE)
-                               size = ENTRIES_PER_PAGE;
-                       freed = shmem_map_and_free_swp(subdir,
-                                       offset, size, &dir, punch_lock);
-                       if (!dir)
-                               dir = shmem_dir_map(middir);
-                       nr_swaps_freed += freed;
-                       if (offset || punch_lock) {
-                               spin_lock(&info->lock);
-                               set_page_private(subdir,
-                                       page_private(subdir) - freed);
-                               spin_unlock(&info->lock);
-                       } else
-                               BUG_ON(page_private(subdir) != freed);
+                       unlock_page(page);
                }
-               offset = 0;
-       }
-done1:
-       shmem_dir_unmap(dir);
-done2:
-       if (inode->i_mapping->nrpages && (info->flags & SHMEM_PAGEIN)) {
-               /*
-                * Call truncate_inode_pages again: racing shmem_unuse_inode
-                * may have swizzled a page in from swap since
-                * truncate_pagecache or generic_delete_inode did it, before we
-                * lowered next_index.  Also, though shmem_getpage checks
-                * i_size before adding to cache, no recheck after: so fix the
-                * narrow window there too.
-                */
-               truncate_inode_pages_range(inode->i_mapping, start, end);
+               shmem_pagevec_release(&pvec);
+               mem_cgroup_uncharge_end();
+               index++;
        }
 
        spin_lock(&info->lock);
-       info->flags &= ~SHMEM_TRUNCATE;
        info->swapped -= nr_swaps_freed;
-       if (nr_pages_to_free)
-               shmem_free_blocks(inode, nr_pages_to_free);
        shmem_recalc_inode(inode);
        spin_unlock(&info->lock);
 
-       /*
-        * Empty swap vector directory pages to be freed?
-        */
-       if (!list_empty(&pages_to_free)) {
-               pages_to_free.prev->next = NULL;
-               shmem_free_pages(pages_to_free.next);
-       }
+       inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 }
 EXPORT_SYMBOL_GPL(shmem_truncate_range);
 
@@ -780,37 +520,7 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr)
        if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
                loff_t oldsize = inode->i_size;
                loff_t newsize = attr->ia_size;
-               struct page *page = NULL;
 
-               if (newsize < oldsize) {
-                       /*
-                        * If truncating down to a partial page, then
-                        * if that page is already allocated, hold it
-                        * in memory until the truncation is over, so
-                        * truncate_partial_page cannot miss it were
-                        * it assigned to swap.
-                        */
-                       if (newsize & (PAGE_CACHE_SIZE-1)) {
-                               (void) shmem_getpage(inode,
-                                       newsize >> PAGE_CACHE_SHIFT,
-                                               &page, SGP_READ, NULL);
-                               if (page)
-                                       unlock_page(page);
-                       }
-                       /*
-                        * Reset SHMEM_PAGEIN flag so that shmem_truncate can
-                        * detect if any pages might have been added to cache
-                        * after truncate_inode_pages.  But we needn't bother
-                        * if it's being fully truncated to zero-length: the
-                        * nrpages check is efficient enough in that case.
-                        */
-                       if (newsize) {
-                               struct shmem_inode_info *info = SHMEM_I(inode);
-                               spin_lock(&info->lock);
-                               info->flags &= ~SHMEM_PAGEIN;
-                               spin_unlock(&info->lock);
-                       }
-               }
                if (newsize != oldsize) {
                        i_size_write(inode, newsize);
                        inode->i_ctime = inode->i_mtime = CURRENT_TIME;
@@ -822,8 +532,6 @@ static int shmem_setattr(struct dentry *dentry, struct iattr *attr)
                        /* unmap again to remove racily COWed private pages */
                        unmap_mapping_range(inode->i_mapping, holebegin, 0, 1);
                }
-               if (page)
-                       page_cache_release(page);
        }
 
        setattr_copy(inode, attr);
@@ -848,7 +556,8 @@ static void shmem_evict_inode(struct inode *inode)
                        list_del_init(&info->swaplist);
                        mutex_unlock(&shmem_swaplist_mutex);
                }
-       }
+       } else
+               kfree(info->symlink);
 
        list_for_each_entry_safe(xattr, nxattr, &info->xattr_list, list) {
                kfree(xattr->name);
@@ -859,106 +568,27 @@ static void shmem_evict_inode(struct inode *inode)
        end_writeback(inode);
 }
 
-static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *dir, swp_entry_t *edir)
-{
-       swp_entry_t *ptr;
-
-       for (ptr = dir; ptr < edir; ptr++) {
-               if (ptr->val == entry.val)
-                       return ptr - dir;
-       }
-       return -1;
-}
-
-static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, struct page *page)
+/*
+ * If swap found in inode, free it and move page from swapcache to filecache.
+ */
+static int shmem_unuse_inode(struct shmem_inode_info *info,
+                            swp_entry_t swap, struct page *page)
 {
-       struct address_space *mapping;
-       unsigned long idx;
-       unsigned long size;
-       unsigned long limit;
-       unsigned long stage;
-       struct page **dir;
-       struct page *subdir;
-       swp_entry_t *ptr;
-       int offset;
+       struct address_space *mapping = info->vfs_inode.i_mapping;
+       void *radswap;
+       pgoff_t index;
        int error;
 
-       idx = 0;
-       ptr = info->i_direct;
-       spin_lock(&info->lock);
-       if (!info->swapped) {
-               list_del_init(&info->swaplist);
-               goto lost2;
-       }
-       limit = info->next_index;
-       size = limit;
-       if (size > SHMEM_NR_DIRECT)
-               size = SHMEM_NR_DIRECT;
-       offset = shmem_find_swp(entry, ptr, ptr+size);
-       if (offset >= 0) {
-               shmem_swp_balance_unmap();
-               goto found;
-       }
-       if (!info->i_indirect)
-               goto lost2;
-
-       dir = shmem_dir_map(info->i_indirect);
-       stage = SHMEM_NR_DIRECT + ENTRIES_PER_PAGEPAGE/2;
-
-       for (idx = SHMEM_NR_DIRECT; idx < limit; idx += ENTRIES_PER_PAGE, dir++) {
-               if (unlikely(idx == stage)) {
-                       shmem_dir_unmap(dir-1);
-                       if (cond_resched_lock(&info->lock)) {
-                               /* check it has not been truncated */
-                               if (limit > info->next_index) {
-                                       limit = info->next_index;
-                                       if (idx >= limit)
-                                               goto lost2;
-                               }
-                       }
-                       dir = shmem_dir_map(info->i_indirect) +
-                           ENTRIES_PER_PAGE/2 + idx/ENTRIES_PER_PAGEPAGE;
-                       while (!*dir) {
-                               dir++;
-                               idx += ENTRIES_PER_PAGEPAGE;
-                               if (idx >= limit)
-                                       goto lost1;
-                       }
-                       stage = idx + ENTRIES_PER_PAGEPAGE;
-                       subdir = *dir;
-                       shmem_dir_unmap(dir);
-                       dir = shmem_dir_map(subdir);
-               }
-               subdir = *dir;
-               if (subdir && page_private(subdir)) {
-                       ptr = shmem_swp_map(subdir);
-                       size = limit - idx;
-                       if (size > ENTRIES_PER_PAGE)
-                               size = ENTRIES_PER_PAGE;
-                       offset = shmem_find_swp(entry, ptr, ptr+size);
-                       shmem_swp_unmap(ptr);
-                       if (offset >= 0) {
-                               shmem_dir_unmap(dir);
-                               ptr = shmem_swp_map(subdir);
-                               goto found;
-                       }
-               }
-       }
-lost1:
-       shmem_dir_unmap(dir-1);
-lost2:
-       spin_unlock(&info->lock);
-       return 0;
-found:
-       idx += offset;
-       ptr += offset;
+       radswap = swp_to_radix_entry(swap);
+       index = radix_tree_locate_item(&mapping->page_tree, radswap);
+       if (index == -1)
+               return 0;
 
        /*
         * Move _head_ to start search for next from here.
         * But be careful: shmem_evict_inode checks list_empty without taking
         * mutex, and there's an instant in list_move_tail when info->swaplist
-        * would appear empty, if it were the only one on shmem_swaplist.  We
-        * could avoid doing it if inode NULL; or use this minor optimization.
+        * would appear empty, if it were the only one on shmem_swaplist.
         */
        if (shmem_swaplist.next != &info->swaplist)
                list_move_tail(&shmem_swaplist, &info->swaplist);
@@ -968,29 +598,34 @@ found:
         * but also to hold up shmem_evict_inode(): so inode cannot be freed
         * beneath us (pagelock doesn't help until the page is in pagecache).
         */
-       mapping = info->vfs_inode.i_mapping;
-       error = add_to_page_cache_locked(page, mapping, idx, GFP_NOWAIT);
+       error = shmem_add_to_page_cache(page, mapping, index,
+                                               GFP_NOWAIT, radswap);
        /* which does mem_cgroup_uncharge_cache_page on error */
 
        if (error != -ENOMEM) {
+               /*
+                * Truncation and eviction use free_swap_and_cache(), which
+                * only does trylock page: if we raced, best clean up here.
+                */
                delete_from_swap_cache(page);
                set_page_dirty(page);
-               info->flags |= SHMEM_PAGEIN;
-               shmem_swp_set(info, ptr, 0);
-               swap_free(entry);
+               if (!error) {
+                       spin_lock(&info->lock);
+                       info->swapped--;
+                       spin_unlock(&info->lock);
+                       swap_free(swap);
+               }
                error = 1;      /* not an error, but entry was found */
        }
-       shmem_swp_unmap(ptr);
-       spin_unlock(&info->lock);
        return error;
 }
 
 /*
- * shmem_unuse() search for an eventually swapped out shmem page.
+ * Search through swapped inodes to find and replace swap by page.
  */
-int shmem_unuse(swp_entry_t entry, struct page *page)
+int shmem_unuse(swp_entry_t swap, struct page *page)
 {
-       struct list_head *p, *next;
+       struct list_head *this, *next;
        struct shmem_inode_info *info;
        int found = 0;
        int error;
@@ -999,32 +634,25 @@ int shmem_unuse(swp_entry_t entry, struct page *page)
         * Charge page using GFP_KERNEL while we can wait, before taking
         * the shmem_swaplist_mutex which might hold up shmem_writepage().
         * Charged back to the user (not to caller) when swap account is used.
-        * add_to_page_cache() will be called with GFP_NOWAIT.
         */
        error = mem_cgroup_cache_charge(page, current->mm, GFP_KERNEL);
        if (error)
                goto out;
-       /*
-        * Try to preload while we can wait, to not make a habit of
-        * draining atomic reserves; but don't latch on to this cpu,
-        * it's okay if sometimes we get rescheduled after this.
-        */
-       error = radix_tree_preload(GFP_KERNEL);
-       if (error)
-               goto uncharge;
-       radix_tree_preload_end();
+       /* No radix_tree_preload: swap entry keeps a place for page in tree */
 
        mutex_lock(&shmem_swaplist_mutex);
-       list_for_each_safe(p, next, &shmem_swaplist) {
-               info = list_entry(p, struct shmem_inode_info, swaplist);
-               found = shmem_unuse_inode(info, entry, page);
+       list_for_each_safe(this, next, &shmem_swaplist) {
+               info = list_entry(this, struct shmem_inode_info, swaplist);
+               if (info->swapped)
+                       found = shmem_unuse_inode(info, swap, page);
+               else
+                       list_del_init(&info->swaplist);
                cond_resched();
                if (found)
                        break;
        }
        mutex_unlock(&shmem_swaplist_mutex);
 
-uncharge:
        if (!found)
                mem_cgroup_uncharge_cache_page(page);
        if (found < 0)
@@ -1041,10 +669,10 @@ out:
 static int shmem_writepage(struct page *page, struct writeback_control *wbc)
 {
        struct shmem_inode_info *info;
-       swp_entry_t *entry, swap;
        struct address_space *mapping;
-       unsigned long index;
        struct inode *inode;
+       swp_entry_t swap;
+       pgoff_t index;
 
        BUG_ON(!PageLocked(page));
        mapping = page->mapping;
@@ -1073,50 +701,32 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc)
 
        /*
         * Add inode to shmem_unuse()'s list of swapped-out inodes,
-        * if it's not already there.  Do it now because we cannot take
-        * mutex while holding spinlock, and must do so before the page
-        * is moved to swap cache, when its pagelock no longer protects
+        * if it's not already there.  Do it now before the page is
+        * moved to swap cache, when its pagelock no longer protects
         * the inode from eviction.  But don't unlock the mutex until
-        * we've taken the spinlock, because shmem_unuse_inode() will
-        * prune a !swapped inode from the swaplist under both locks.
+        * we've incremented swapped, because shmem_unuse_inode() will
+        * prune a !swapped inode from the swaplist under this mutex.
         */
        mutex_lock(&shmem_swaplist_mutex);
        if (list_empty(&info->swaplist))
                list_add_tail(&info->swaplist, &shmem_swaplist);
 
-       spin_lock(&info->lock);
-       mutex_unlock(&shmem_swaplist_mutex);
-
-       if (index >= info->next_index) {
-               BUG_ON(!(info->flags & SHMEM_TRUNCATE));
-               goto unlock;
-       }
-       entry = shmem_swp_entry(info, index, NULL);
-       if (entry->val) {
-               WARN_ON_ONCE(1);        /* Still happens? Tell us about it! */
-               free_swap_and_cache(*entry);
-               shmem_swp_set(info, entry, 0);
-       }
-       shmem_recalc_inode(inode);
-
        if (add_to_swap_cache(page, swap, GFP_ATOMIC) == 0) {
-               delete_from_page_cache(page);
-               shmem_swp_set(info, entry, swap.val);
-               shmem_swp_unmap(entry);
                swap_shmem_alloc(swap);
+               shmem_delete_from_page_cache(page, swp_to_radix_entry(swap));
+
+               spin_lock(&info->lock);
+               info->swapped++;
+               shmem_recalc_inode(inode);
                spin_unlock(&info->lock);
+
+               mutex_unlock(&shmem_swaplist_mutex);
                BUG_ON(page_mapped(page));
                swap_writepage(page, wbc);
                return 0;
        }
 
-       shmem_swp_unmap(entry);
-unlock:
-       spin_unlock(&info->lock);
-       /*
-        * add_to_swap_cache() doesn't return -EEXIST, so we can safely
-        * clear SWAP_HAS_CACHE flag.
-        */
+       mutex_unlock(&shmem_swaplist_mutex);
        swapcache_free(swap, NULL);
 redirty:
        set_page_dirty(page);
@@ -1153,35 +763,33 @@ static struct mempolicy *shmem_get_sbmpol(struct shmem_sb_info *sbinfo)
 }
 #endif /* CONFIG_TMPFS */
 
-static struct page *shmem_swapin(swp_entry_t entry, gfp_t gfp,
-                       struct shmem_inode_info *info, unsigned long idx)
+static struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp,
+                       struct shmem_inode_info *info, pgoff_t index)
 {
        struct mempolicy mpol, *spol;
        struct vm_area_struct pvma;
-       struct page *page;
 
        spol = mpol_cond_copy(&mpol,
-                               mpol_shared_policy_lookup(&info->policy, idx));
+                       mpol_shared_policy_lookup(&info->policy, index));
 
        /* Create a pseudo vma that just contains the policy */
        pvma.vm_start = 0;
-       pvma.vm_pgoff = idx;
+       pvma.vm_pgoff = index;
        pvma.vm_ops = NULL;
        pvma.vm_policy = spol;
-       page = swapin_readahead(entry, gfp, &pvma, 0);
-       return page;
+       return swapin_readahead(swap, gfp, &pvma, 0);
 }
 
 static struct page *shmem_alloc_page(gfp_t gfp,
-                       struct shmem_inode_info *info, unsigned long idx)
+                       struct shmem_inode_info *info, pgoff_t index)
 {
        struct vm_area_struct pvma;
 
        /* Create a pseudo vma that just contains the policy */
        pvma.vm_start = 0;
-       pvma.vm_pgoff = idx;
+       pvma.vm_pgoff = index;
        pvma.vm_ops = NULL;
-       pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, idx);
+       pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, index);
 
        /*
         * alloc_page_vma() will drop the shared policy reference
@@ -1190,19 +798,19 @@ static struct page *shmem_alloc_page(gfp_t gfp,
 }
 #else /* !CONFIG_NUMA */
 #ifdef CONFIG_TMPFS
-static inline void shmem_show_mpol(struct seq_file *seq, struct mempolicy *p)
+static inline void shmem_show_mpol(struct seq_file *seq, struct mempolicy *mpol)
 {
 }
 #endif /* CONFIG_TMPFS */
 
-static inline struct page *shmem_swapin(swp_entry_t entry, gfp_t gfp,
-                       struct shmem_inode_info *info, unsigned long idx)
+static inline struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp,
+                       struct shmem_inode_info *info, pgoff_t index)
 {
-       return swapin_readahead(entry, gfp, NULL, 0);
+       return swapin_readahead(swap, gfp, NULL, 0);
 }
 
 static inline struct page *shmem_alloc_page(gfp_t gfp,
-                       struct shmem_inode_info *info, unsigned long idx)
+                       struct shmem_inode_info *info, pgoff_t index)
 {
        return alloc_page(gfp);
 }
@@ -1222,243 +830,190 @@ static inline struct mempolicy *shmem_get_sbmpol(struct shmem_sb_info *sbinfo)
  * vm. If we swap it in we mark it dirty since we also free the swap
  * entry since a page cannot live in both the swap and page cache
  */
-static int shmem_getpage_gfp(struct inode *inode, pgoff_t idx,
+static int shmem_getpage_gfp(struct inode *inode, pgoff_t index,
        struct page **pagep, enum sgp_type sgp, gfp_t gfp, int *fault_type)
 {
        struct address_space *mapping = inode->i_mapping;
-       struct shmem_inode_info *info = SHMEM_I(inode);
+       struct shmem_inode_info *info;
        struct shmem_sb_info *sbinfo;
        struct page *page;
-       struct page *prealloc_page = NULL;
-       swp_entry_t *entry;
        swp_entry_t swap;
        int error;
-       int ret;
+       int once = 0;
 
-       if (idx >= SHMEM_MAX_INDEX)
+       if (index > (MAX_LFS_FILESIZE >> PAGE_CACHE_SHIFT))
                return -EFBIG;
 repeat:
-       page = find_lock_page(mapping, idx);
-       if (page) {
+       swap.val = 0;
+       page = find_lock_page(mapping, index);
+       if (radix_tree_exceptional_entry(page)) {
+               swap = radix_to_swp_entry(page);
+               page = NULL;
+       }
+
+       if (sgp != SGP_WRITE &&
+           ((loff_t)index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
+               error = -EINVAL;
+               goto failed;
+       }
+
+       if (page || (sgp == SGP_READ && !swap.val)) {
                /*
                 * Once we can get the page lock, it must be uptodate:
                 * if there were an error in reading back from swap,
                 * the page would not be inserted into the filecache.
                 */
-               BUG_ON(!PageUptodate(page));
-               goto done;
+               BUG_ON(page && !PageUptodate(page));
+               *pagep = page;
+               return 0;
        }
 
        /*
-        * Try to preload while we can wait, to not make a habit of
-        * draining atomic reserves; but don't latch on to this cpu.
+        * Fast cache lookup did not find it:
+        * bring it back from swap or allocate.
         */
-       error = radix_tree_preload(gfp & GFP_RECLAIM_MASK);
-       if (error)
-               goto out;
-       radix_tree_preload_end();
-
-       if (sgp != SGP_READ && !prealloc_page) {
-               prealloc_page = shmem_alloc_page(gfp, info, idx);
-               if (prealloc_page) {
-                       SetPageSwapBacked(prealloc_page);
-                       if (mem_cgroup_cache_charge(prealloc_page,
-                                       current->mm, GFP_KERNEL)) {
-                               page_cache_release(prealloc_page);
-                               prealloc_page = NULL;
-                       }
-               }
-       }
-
-       spin_lock(&info->lock);
-       shmem_recalc_inode(inode);
-       entry = shmem_swp_alloc(info, idx, sgp, gfp);
-       if (IS_ERR(entry)) {
-               spin_unlock(&info->lock);
-               error = PTR_ERR(entry);
-               goto out;
-       }
-       swap = *entry;
+       info = SHMEM_I(inode);
+       sbinfo = SHMEM_SB(inode->i_sb);
 
        if (swap.val) {
                /* Look it up and read it in.. */
                page = lookup_swap_cache(swap);
                if (!page) {
-                       shmem_swp_unmap(entry);
-                       spin_unlock(&info->lock);
                        /* here we actually do the io */
                        if (fault_type)
                                *fault_type |= VM_FAULT_MAJOR;
-                       page = shmem_swapin(swap, gfp, info, idx);
+                       page = shmem_swapin(swap, gfp, info, index);
                        if (!page) {
-                               spin_lock(&info->lock);
-                               entry = shmem_swp_alloc(info, idx, sgp, gfp);
-                               if (IS_ERR(entry))
-                                       error = PTR_ERR(entry);
-                               else {
-                                       if (entry->val == swap.val)
-                                               error = -ENOMEM;
-                                       shmem_swp_unmap(entry);
-                               }
-                               spin_unlock(&info->lock);
-                               if (error)
-                                       goto out;
-                               goto repeat;
+                               error = -ENOMEM;
+                               goto failed;
                        }
-                       wait_on_page_locked(page);
-                       page_cache_release(page);
-                       goto repeat;
                }
 
                /* We have to do this with page locked to prevent races */
-               if (!trylock_page(page)) {
-                       shmem_swp_unmap(entry);
-                       spin_unlock(&info->lock);
-                       wait_on_page_locked(page);
-                       page_cache_release(page);
-                       goto repeat;
-               }
-               if (PageWriteback(page)) {
-                       shmem_swp_unmap(entry);
-                       spin_unlock(&info->lock);
-                       wait_on_page_writeback(page);
-                       unlock_page(page);
-                       page_cache_release(page);
-                       goto repeat;
-               }
+               lock_page(page);
                if (!PageUptodate(page)) {
-                       shmem_swp_unmap(entry);
-                       spin_unlock(&info->lock);
-                       unlock_page(page);
-                       page_cache_release(page);
                        error = -EIO;
-                       goto out;
+                       goto failed;
                }
-
-               error = add_to_page_cache_locked(page, mapping,
-                                                idx, GFP_NOWAIT);
-               if (error) {
-                       shmem_swp_unmap(entry);
-                       spin_unlock(&info->lock);
-                       if (error == -ENOMEM) {
-                               /*
-                                * reclaim from proper memory cgroup and
-                                * call memcg's OOM if needed.
-                                */
-                               error = mem_cgroup_shmem_charge_fallback(
-                                               page, current->mm, gfp);
-                               if (error) {
-                                       unlock_page(page);
-                                       page_cache_release(page);
-                                       goto out;
-                               }
-                       }
-                       unlock_page(page);
-                       page_cache_release(page);
-                       goto repeat;
+               wait_on_page_writeback(page);
+
+               /* Someone may have already done it for us */
+               if (page->mapping) {
+                       if (page->mapping == mapping &&
+                           page->index == index)
+                               goto done;
+                       error = -EEXIST;
+                       goto failed;
                }
 
-               info->flags |= SHMEM_PAGEIN;
-               shmem_swp_set(info, entry, 0);
-               shmem_swp_unmap(entry);
-               delete_from_swap_cache(page);
+               error = mem_cgroup_cache_charge(page, current->mm,
+                                               gfp & GFP_RECLAIM_MASK);
+               if (!error)
+                       error = shmem_add_to_page_cache(page, mapping, index,
+                                               gfp, swp_to_radix_entry(swap));
+               if (error)
+                       goto failed;
+
+               spin_lock(&info->lock);
+               info->swapped--;
+               shmem_recalc_inode(inode);
                spin_unlock(&info->lock);
+
+               delete_from_swap_cache(page);
                set_page_dirty(page);
                swap_free(swap);
 
-       } else if (sgp == SGP_READ) {
-               shmem_swp_unmap(entry);
-               page = find_get_page(mapping, idx);
-               if (page && !trylock_page(page)) {
-                       spin_unlock(&info->lock);
-                       wait_on_page_locked(page);
-                       page_cache_release(page);
-                       goto repeat;
+       } else {
+               if (shmem_acct_block(info->flags)) {
+                       error = -ENOSPC;
+                       goto failed;
                }
-               spin_unlock(&info->lock);
-
-       } else if (prealloc_page) {
-               shmem_swp_unmap(entry);
-               sbinfo = SHMEM_SB(inode->i_sb);
                if (sbinfo->max_blocks) {
                        if (percpu_counter_compare(&sbinfo->used_blocks,
-                                               sbinfo->max_blocks) >= 0 ||
-                           shmem_acct_block(info->flags))
-                               goto nospace;
+                                               sbinfo->max_blocks) >= 0) {
+                               error = -ENOSPC;
+                               goto unacct;
+                       }
                        percpu_counter_inc(&sbinfo->used_blocks);
-                       inode->i_blocks += BLOCKS_PER_PAGE;
-               } else if (shmem_acct_block(info->flags))
-                       goto nospace;
-
-               page = prealloc_page;
-               prealloc_page = NULL;
-
-               entry = shmem_swp_alloc(info, idx, sgp, gfp);
-               if (IS_ERR(entry))
-                       error = PTR_ERR(entry);
-               else {
-                       swap = *entry;
-                       shmem_swp_unmap(entry);
                }
-               ret = error || swap.val;
-               if (ret)
-                       mem_cgroup_uncharge_cache_page(page);
-               else
-                       ret = add_to_page_cache_lru(page, mapping,
-                                               idx, GFP_NOWAIT);
-               /*
-                * At add_to_page_cache_lru() failure,
-                * uncharge will be done automatically.
-                */
-               if (ret) {
-                       shmem_unacct_blocks(info->flags, 1);
-                       shmem_free_blocks(inode, 1);
-                       spin_unlock(&info->lock);
-                       page_cache_release(page);
-                       if (error)
-                               goto out;
-                       goto repeat;
+
+               page = shmem_alloc_page(gfp, info, index);
+               if (!page) {
+                       error = -ENOMEM;
+                       goto decused;
                }
 
-               info->flags |= SHMEM_PAGEIN;
+               SetPageSwapBacked(page);
+               __set_page_locked(page);
+               error = mem_cgroup_cache_charge(page, current->mm,
+                                               gfp & GFP_RECLAIM_MASK);
+               if (!error)
+                       error = shmem_add_to_page_cache(page, mapping, index,
+                                               gfp, NULL);
+               if (error)
+                       goto decused;
+               lru_cache_add_anon(page);
+
+               spin_lock(&info->lock);
                info->alloced++;
+               inode->i_blocks += BLOCKS_PER_PAGE;
+               shmem_recalc_inode(inode);
                spin_unlock(&info->lock);
+
                clear_highpage(page);
                flush_dcache_page(page);
                SetPageUptodate(page);
                if (sgp == SGP_DIRTY)
                        set_page_dirty(page);
-
-       } else {
-               spin_unlock(&info->lock);
-               error = -ENOMEM;
-               goto out;
        }
 done:
-       *pagep = page;
-       error = 0;
-out:
-       if (prealloc_page) {
-               mem_cgroup_uncharge_cache_page(prealloc_page);
-               page_cache_release(prealloc_page);
+       /* Perhaps the file has been truncated since we checked */
+       if (sgp != SGP_WRITE &&
+           ((loff_t)index << PAGE_CACHE_SHIFT) >= i_size_read(inode)) {
+               error = -EINVAL;
+               goto trunc;
        }
-       return error;
+       *pagep = page;
+       return 0;
 
-nospace:
        /*
-        * Perhaps the page was brought in from swap between find_lock_page
-        * and taking info->lock?  We allow for that at add_to_page_cache_lru,
-        * but must also avoid reporting a spurious ENOSPC while working on a
-        * full tmpfs.
+        * Error recovery.
         */
-       page = find_get_page(mapping, idx);
+trunc:
+       ClearPageDirty(page);
+       delete_from_page_cache(page);
+       spin_lock(&info->lock);
+       info->alloced--;
+       inode->i_blocks -= BLOCKS_PER_PAGE;
        spin_unlock(&info->lock);
+decused:
+       if (sbinfo->max_blocks)
+               percpu_counter_add(&sbinfo->used_blocks, -1);
+unacct:
+       shmem_unacct_blocks(info->flags, 1);
+failed:
+       if (swap.val && error != -EINVAL) {
+               struct page *test = find_get_page(mapping, index);
+               if (test && !radix_tree_exceptional_entry(test))
+                       page_cache_release(test);
+               /* Have another try if the entry has changed */
+               if (test != swp_to_radix_entry(swap))
+                       error = -EEXIST;
+       }
        if (page) {
+               unlock_page(page);
                page_cache_release(page);
+       }
+       if (error == -ENOSPC && !once++) {
+               info = SHMEM_I(inode);
+               spin_lock(&info->lock);
+               shmem_recalc_inode(inode);
+               spin_unlock(&info->lock);
                goto repeat;
        }
-       error = -ENOSPC;
-       goto out;
+       if (error == -EEXIST)
+               goto repeat;
+       return error;
 }
 
 static int shmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
@@ -1467,9 +1022,6 @@ static int shmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        int error;
        int ret = VM_FAULT_LOCKED;
 
-       if (((loff_t)vmf->pgoff << PAGE_CACHE_SHIFT) >= i_size_read(inode))
-               return VM_FAULT_SIGBUS;
-
        error = shmem_getpage(inode, vmf->pgoff, &vmf->page, SGP_CACHE, &ret);
        if (error)
                return ((error == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS);
@@ -1482,20 +1034,20 @@ static int shmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 }
 
 #ifdef CONFIG_NUMA
-static int shmem_set_policy(struct vm_area_struct *vma, struct mempolicy *new)
+static int shmem_set_policy(struct vm_area_struct *vma, struct mempolicy *mpol)
 {
-       struct inode *i = vma->vm_file->f_path.dentry->d_inode;
-       return mpol_set_shared_policy(&SHMEM_I(i)->policy, vma, new);
+       struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
+       return mpol_set_shared_policy(&SHMEM_I(inode)->policy, vma, mpol);
 }
 
 static struct mempolicy *shmem_get_policy(struct vm_area_struct *vma,
                                          unsigned long addr)
 {
-       struct inode *i = vma->vm_file->f_path.dentry->d_inode;
-       unsigned long idx;
+       struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
+       pgoff_t index;
 
-       idx = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
-       return mpol_shared_policy_lookup(&SHMEM_I(i)->policy, idx);
+       index = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+       return mpol_shared_policy_lookup(&SHMEM_I(inode)->policy, index);
 }
 #endif
 
@@ -1593,7 +1145,7 @@ static struct inode *shmem_get_inode(struct super_block *sb, const struct inode
 
 #ifdef CONFIG_TMPFS
 static const struct inode_operations shmem_symlink_inode_operations;
-static const struct inode_operations shmem_symlink_inline_operations;
+static const struct inode_operations shmem_short_symlink_operations;
 
 static int
 shmem_write_begin(struct file *file, struct address_space *mapping,
@@ -1626,7 +1178,8 @@ static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_
 {
        struct inode *inode = filp->f_path.dentry->d_inode;
        struct address_space *mapping = inode->i_mapping;
-       unsigned long index, offset;
+       pgoff_t index;
+       unsigned long offset;
        enum sgp_type sgp = SGP_READ;
 
        /*
@@ -1642,7 +1195,8 @@ static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_
 
        for (;;) {
                struct page *page = NULL;
-               unsigned long end_index, nr, ret;
+               pgoff_t end_index;
+               unsigned long nr, ret;
                loff_t i_size = i_size_read(inode);
 
                end_index = i_size >> PAGE_CACHE_SHIFT;
@@ -1880,8 +1434,9 @@ static int shmem_statfs(struct dentry *dentry, struct kstatfs *buf)
        buf->f_namelen = NAME_MAX;
        if (sbinfo->max_blocks) {
                buf->f_blocks = sbinfo->max_blocks;
-               buf->f_bavail = buf->f_bfree =
-                               sbinfo->max_blocks - percpu_counter_sum(&sbinfo->used_blocks);
+               buf->f_bavail =
+               buf->f_bfree  = sbinfo->max_blocks -
+                               percpu_counter_sum(&sbinfo->used_blocks);
        }
        if (sbinfo->max_inodes) {
                buf->f_files = sbinfo->max_inodes;
@@ -2055,10 +1610,13 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
 
        info = SHMEM_I(inode);
        inode->i_size = len-1;
-       if (len <= SHMEM_SYMLINK_INLINE_LEN) {
-               /* do it inline */
-               memcpy(info->inline_symlink, symname, len);
-               inode->i_op = &shmem_symlink_inline_operations;
+       if (len <= SHORT_SYMLINK_LEN) {
+               info->symlink = kmemdup(symname, len, GFP_KERNEL);
+               if (!info->symlink) {
+                       iput(inode);
+                       return -ENOMEM;
+               }
+               inode->i_op = &shmem_short_symlink_operations;
        } else {
                error = shmem_getpage(inode, 0, &page, SGP_WRITE, NULL);
                if (error) {
@@ -2081,17 +1639,17 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
        return 0;
 }
 
-static void *shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd)
+static void *shmem_follow_short_symlink(struct dentry *dentry, struct nameidata *nd)
 {
-       nd_set_link(nd, SHMEM_I(dentry->d_inode)->inline_symlink);
+       nd_set_link(nd, SHMEM_I(dentry->d_inode)->symlink);
        return NULL;
 }
 
 static void *shmem_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
        struct page *page = NULL;
-       int res = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ, NULL);
-       nd_set_link(nd, res ? ERR_PTR(res) : kmap(page));
+       int error = shmem_getpage(dentry->d_inode, 0, &page, SGP_READ, NULL);
+       nd_set_link(nd, error ? ERR_PTR(error) : kmap(page));
        if (page)
                unlock_page(page);
        return page;
@@ -2202,7 +1760,6 @@ out:
        return err;
 }
 
-
 static const struct xattr_handler *shmem_xattr_handlers[] = {
 #ifdef CONFIG_TMPFS_POSIX_ACL
        &generic_acl_access_handler,
@@ -2332,9 +1889,9 @@ static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size)
 }
 #endif /* CONFIG_TMPFS_XATTR */
 
-static const struct inode_operations shmem_symlink_inline_operations = {
+static const struct inode_operations shmem_short_symlink_operations = {
        .readlink       = generic_readlink,
-       .follow_link    = shmem_follow_link_inline,
+       .follow_link    = shmem_follow_short_symlink,
 #ifdef CONFIG_TMPFS_XATTR
        .setxattr       = shmem_setxattr,
        .getxattr       = shmem_getxattr,
@@ -2534,8 +2091,7 @@ static int shmem_remount_fs(struct super_block *sb, int *flags, char *data)
        if (config.max_inodes < inodes)
                goto out;
        /*
-        * Those tests also disallow limited->unlimited while any are in
-        * use, so i_blocks will always be zero when max_blocks is zero;
+        * Those tests disallow limited->unlimited while any are in use;
         * but we must separately disallow unlimited->limited, because
         * in that case we have no record of how much is already in use.
         */
@@ -2627,7 +2183,7 @@ int shmem_fill_super(struct super_block *sb, void *data, int silent)
                goto failed;
        sbinfo->free_inodes = sbinfo->max_inodes;
 
-       sb->s_maxbytes = SHMEM_MAX_BYTES;
+       sb->s_maxbytes = MAX_LFS_FILESIZE;
        sb->s_blocksize = PAGE_CACHE_SIZE;
        sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
        sb->s_magic = TMPFS_MAGIC;
@@ -2662,14 +2218,14 @@ static struct kmem_cache *shmem_inode_cachep;
 
 static struct inode *shmem_alloc_inode(struct super_block *sb)
 {
-       struct shmem_inode_info *p;
-       p = (struct shmem_inode_info *)kmem_cache_alloc(shmem_inode_cachep, GFP_KERNEL);
-       if (!p)
+       struct shmem_inode_info *info;
+       info = kmem_cache_alloc(shmem_inode_cachep, GFP_KERNEL);
+       if (!info)
                return NULL;
-       return &p->vfs_inode;
+       return &info->vfs_inode;
 }
 
-static void shmem_i_callback(struct rcu_head *head)
+static void shmem_destroy_callback(struct rcu_head *head)
 {
        struct inode *inode = container_of(head, struct inode, i_rcu);
        INIT_LIST_HEAD(&inode->i_dentry);
@@ -2678,29 +2234,26 @@ static void shmem_i_callback(struct rcu_head *head)
 
 static void shmem_destroy_inode(struct inode *inode)
 {
-       if ((inode->i_mode & S_IFMT) == S_IFREG) {
-               /* only struct inode is valid if it's an inline symlink */
+       if ((inode->i_mode & S_IFMT) == S_IFREG)
                mpol_free_shared_policy(&SHMEM_I(inode)->policy);
-       }
-       call_rcu(&inode->i_rcu, shmem_i_callback);
+       call_rcu(&inode->i_rcu, shmem_destroy_callback);
 }
 
-static void init_once(void *foo)
+static void shmem_init_inode(void *foo)
 {
-       struct shmem_inode_info *p = (struct shmem_inode_info *) foo;
-
-       inode_init_once(&p->vfs_inode);
+       struct shmem_inode_info *info = foo;
+       inode_init_once(&info->vfs_inode);
 }
 
-static int init_inodecache(void)
+static int shmem_init_inodecache(void)
 {
        shmem_inode_cachep = kmem_cache_create("shmem_inode_cache",
                                sizeof(struct shmem_inode_info),
-                               0, SLAB_PANIC, init_once);
+                               0, SLAB_PANIC, shmem_init_inode);
        return 0;
 }
 
-static void destroy_inodecache(void)
+static void shmem_destroy_inodecache(void)
 {
        kmem_cache_destroy(shmem_inode_cachep);
 }
@@ -2797,21 +2350,20 @@ static const struct vm_operations_struct shmem_vm_ops = {
 #endif
 };
 
-
 static struct dentry *shmem_mount(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *data)
 {
        return mount_nodev(fs_type, flags, data, shmem_fill_super);
 }
 
-static struct file_system_type tmpfs_fs_type = {
+static struct file_system_type shmem_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "tmpfs",
        .mount          = shmem_mount,
        .kill_sb        = kill_litter_super,
 };
 
-int __init init_tmpfs(void)
+int __init shmem_init(void)
 {
        int error;
 
@@ -2819,18 +2371,18 @@ int __init init_tmpfs(void)
        if (error)
                goto out4;
 
-       error = init_inodecache();
+       error = shmem_init_inodecache();
        if (error)
                goto out3;
 
-       error = register_filesystem(&tmpfs_fs_type);
+       error = register_filesystem(&shmem_fs_type);
        if (error) {
                printk(KERN_ERR "Could not register tmpfs\n");
                goto out2;
        }
 
-       shm_mnt = vfs_kern_mount(&tmpfs_fs_type, MS_NOUSER,
-                               tmpfs_fs_type.name, NULL);
+       shm_mnt = vfs_kern_mount(&shmem_fs_type, MS_NOUSER,
+                                shmem_fs_type.name, NULL);
        if (IS_ERR(shm_mnt)) {
                error = PTR_ERR(shm_mnt);
                printk(KERN_ERR "Could not kern_mount tmpfs\n");
@@ -2839,9 +2391,9 @@ int __init init_tmpfs(void)
        return 0;
 
 out1:
-       unregister_filesystem(&tmpfs_fs_type);
+       unregister_filesystem(&shmem_fs_type);
 out2:
-       destroy_inodecache();
+       shmem_destroy_inodecache();
 out3:
        bdi_destroy(&shmem_backing_dev_info);
 out4:
@@ -2849,45 +2401,6 @@ out4:
        return error;
 }
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
-/**
- * mem_cgroup_get_shmem_target - find a page or entry assigned to the shmem file
- * @inode: the inode to be searched
- * @pgoff: the offset to be searched
- * @pagep: the pointer for the found page to be stored
- * @ent: the pointer for the found swap entry to be stored
- *
- * If a page is found, refcount of it is incremented. Callers should handle
- * these refcount.
- */
-void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t pgoff,
-                                       struct page **pagep, swp_entry_t *ent)
-{
-       swp_entry_t entry = { .val = 0 }, *ptr;
-       struct page *page = NULL;
-       struct shmem_inode_info *info = SHMEM_I(inode);
-
-       if ((pgoff << PAGE_CACHE_SHIFT) >= i_size_read(inode))
-               goto out;
-
-       spin_lock(&info->lock);
-       ptr = shmem_swp_entry(info, pgoff, NULL);
-#ifdef CONFIG_SWAP
-       if (ptr && ptr->val) {
-               entry.val = ptr->val;
-               page = find_get_page(&swapper_space, entry.val);
-       } else
-#endif
-               page = find_get_page(inode->i_mapping, pgoff);
-       if (ptr)
-               shmem_swp_unmap(ptr);
-       spin_unlock(&info->lock);
-out:
-       *pagep = page;
-       *ent = entry;
-}
-#endif
-
 #else /* !CONFIG_SHMEM */
 
 /*
@@ -2901,23 +2414,23 @@ out:
 
 #include <linux/ramfs.h>
 
-static struct file_system_type tmpfs_fs_type = {
+static struct file_system_type shmem_fs_type = {
        .name           = "tmpfs",
        .mount          = ramfs_mount,
        .kill_sb        = kill_litter_super,
 };
 
-int __init init_tmpfs(void)
+int __init shmem_init(void)
 {
-       BUG_ON(register_filesystem(&tmpfs_fs_type) != 0);
+       BUG_ON(register_filesystem(&shmem_fs_type) != 0);
 
-       shm_mnt = kern_mount(&tmpfs_fs_type);
+       shm_mnt = kern_mount(&shmem_fs_type);
        BUG_ON(IS_ERR(shm_mnt));
 
        return 0;
 }
 
-int shmem_unuse(swp_entry_t entry, struct page *page)
+int shmem_unuse(swp_entry_t swap, struct page *page)
 {
        return 0;
 }
@@ -2927,43 +2440,17 @@ int shmem_lock(struct file *file, int lock, struct user_struct *user)
        return 0;
 }
 
-void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end)
+void shmem_truncate_range(struct inode *inode, loff_t lstart, loff_t lend)
 {
-       truncate_inode_pages_range(inode->i_mapping, start, end);
+       truncate_inode_pages_range(inode->i_mapping, lstart, lend);
 }
 EXPORT_SYMBOL_GPL(shmem_truncate_range);
 
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
-/**
- * mem_cgroup_get_shmem_target - find a page or entry assigned to the shmem file
- * @inode: the inode to be searched
- * @pgoff: the offset to be searched
- * @pagep: the pointer for the found page to be stored
- * @ent: the pointer for the found swap entry to be stored
- *
- * If a page is found, refcount of it is incremented. Callers should handle
- * these refcount.
- */
-void mem_cgroup_get_shmem_target(struct inode *inode, pgoff_t pgoff,
-                                       struct page **pagep, swp_entry_t *ent)
-{
-       struct page *page = NULL;
-
-       if ((pgoff << PAGE_CACHE_SHIFT) >= i_size_read(inode))
-               goto out;
-       page = find_get_page(inode->i_mapping, pgoff);
-out:
-       *pagep = page;
-       *ent = (swp_entry_t){ .val = 0 };
-}
-#endif
-
 #define shmem_vm_ops                           generic_file_vm_ops
 #define shmem_file_operations                  ramfs_file_operations
 #define shmem_get_inode(sb, dir, mode, dev, flags)     ramfs_get_inode(sb, dir, mode, dev)
 #define shmem_acct_size(flags, size)           0
 #define shmem_unacct_size(flags, size)         do {} while (0)
-#define SHMEM_MAX_BYTES                                MAX_LFS_FILESIZE
 
 #endif /* CONFIG_SHMEM */
 
@@ -2987,7 +2474,7 @@ struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags
        if (IS_ERR(shm_mnt))
                return (void *)shm_mnt;
 
-       if (size < 0 || size > SHMEM_MAX_BYTES)
+       if (size < 0 || size > MAX_LFS_FILESIZE)
                return ERR_PTR(-EINVAL);
 
        if (shmem_acct_size(flags, size))
index 95947400702bad1961fac92ea91a3707713a907a..6d90a091fdca5b2a805fa280d7173775461e25d7 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -622,6 +622,51 @@ int slab_is_available(void)
 static struct lock_class_key on_slab_l3_key;
 static struct lock_class_key on_slab_alc_key;
 
+static struct lock_class_key debugobj_l3_key;
+static struct lock_class_key debugobj_alc_key;
+
+static void slab_set_lock_classes(struct kmem_cache *cachep,
+               struct lock_class_key *l3_key, struct lock_class_key *alc_key,
+               int q)
+{
+       struct array_cache **alc;
+       struct kmem_list3 *l3;
+       int r;
+
+       l3 = cachep->nodelists[q];
+       if (!l3)
+               return;
+
+       lockdep_set_class(&l3->list_lock, l3_key);
+       alc = l3->alien;
+       /*
+        * FIXME: This check for BAD_ALIEN_MAGIC
+        * should go away when common slab code is taught to
+        * work even without alien caches.
+        * Currently, non NUMA code returns BAD_ALIEN_MAGIC
+        * for alloc_alien_cache,
+        */
+       if (!alc || (unsigned long)alc == BAD_ALIEN_MAGIC)
+               return;
+       for_each_node(r) {
+               if (alc[r])
+                       lockdep_set_class(&alc[r]->lock, alc_key);
+       }
+}
+
+static void slab_set_debugobj_lock_classes_node(struct kmem_cache *cachep, int node)
+{
+       slab_set_lock_classes(cachep, &debugobj_l3_key, &debugobj_alc_key, node);
+}
+
+static void slab_set_debugobj_lock_classes(struct kmem_cache *cachep)
+{
+       int node;
+
+       for_each_online_node(node)
+               slab_set_debugobj_lock_classes_node(cachep, node);
+}
+
 static void init_node_lock_keys(int q)
 {
        struct cache_sizes *s = malloc_sizes;
@@ -630,29 +675,14 @@ static void init_node_lock_keys(int q)
                return;
 
        for (s = malloc_sizes; s->cs_size != ULONG_MAX; s++) {
-               struct array_cache **alc;
                struct kmem_list3 *l3;
-               int r;
 
                l3 = s->cs_cachep->nodelists[q];
                if (!l3 || OFF_SLAB(s->cs_cachep))
                        continue;
-               lockdep_set_class(&l3->list_lock, &on_slab_l3_key);
-               alc = l3->alien;
-               /*
-                * FIXME: This check for BAD_ALIEN_MAGIC
-                * should go away when common slab code is taught to
-                * work even without alien caches.
-                * Currently, non NUMA code returns BAD_ALIEN_MAGIC
-                * for alloc_alien_cache,
-                */
-               if (!alc || (unsigned long)alc == BAD_ALIEN_MAGIC)
-                       continue;
-               for_each_node(r) {
-                       if (alc[r])
-                               lockdep_set_class(&alc[r]->lock,
-                                       &on_slab_alc_key);
-               }
+
+               slab_set_lock_classes(s->cs_cachep, &on_slab_l3_key,
+                               &on_slab_alc_key, q);
        }
 }
 
@@ -671,6 +701,14 @@ static void init_node_lock_keys(int q)
 static inline void init_lock_keys(void)
 {
 }
+
+static void slab_set_debugobj_lock_classes_node(struct kmem_cache *cachep, int node)
+{
+}
+
+static void slab_set_debugobj_lock_classes(struct kmem_cache *cachep)
+{
+}
 #endif
 
 /*
@@ -1264,6 +1302,8 @@ static int __cpuinit cpuup_prepare(long cpu)
                spin_unlock_irq(&l3->list_lock);
                kfree(shared);
                free_alien_cache(alien);
+               if (cachep->flags & SLAB_DEBUG_OBJECTS)
+                       slab_set_debugobj_lock_classes_node(cachep, node);
        }
        init_node_lock_keys(node);
 
@@ -1626,6 +1666,9 @@ void __init kmem_cache_init_late(void)
 {
        struct kmem_cache *cachep;
 
+       /* Annotate slab for lockdep -- annotate the malloc caches */
+       init_lock_keys();
+
        /* 6) resize the head arrays to their final sizes */
        mutex_lock(&cache_chain_mutex);
        list_for_each_entry(cachep, &cache_chain, next)
@@ -1636,9 +1679,6 @@ void __init kmem_cache_init_late(void)
        /* Done! */
        g_cpucache_up = FULL;
 
-       /* Annotate slab for lockdep -- annotate the malloc caches */
-       init_lock_keys();
-
        /*
         * Register a cpu startup notifier callback that initializes
         * cpu_cache_get for all new cpus
@@ -2426,6 +2466,16 @@ kmem_cache_create (const char *name, size_t size, size_t align,
                goto oops;
        }
 
+       if (flags & SLAB_DEBUG_OBJECTS) {
+               /*
+                * Would deadlock through slab_destroy()->call_rcu()->
+                * debug_object_activate()->kmem_cache_alloc().
+                */
+               WARN_ON_ONCE(flags & SLAB_DESTROY_BY_RCU);
+
+               slab_set_debugobj_lock_classes(cachep);
+       }
+
        /* cache setup completed, link it into the list */
        list_add(&cachep->next, &cache_chain);
 oops:
index 1b8c33907242f0543e2774bd8e2c46d1577ed4c1..17bc224bce68c618285e9c74a3a2db0a4ec24c0f 100644 (file)
@@ -1924,20 +1924,24 @@ static unsigned long read_swap_header(struct swap_info_struct *p,
 
        /*
         * Find out how many pages are allowed for a single swap
-        * device. There are two limiting factors: 1) the number of
-        * bits for the swap offset in the swp_entry_t type and
-        * 2) the number of bits in the a swap pte as defined by
-        * the different architectures. In order to find the
-        * largest possible bit mask a swap entry with swap type 0
+        * device. There are three limiting factors: 1) the number
+        * of bits for the swap offset in the swp_entry_t type, and
+        * 2) the number of bits in the swap pte as defined by the
+        * the different architectures, and 3) the number of free bits
+        * in an exceptional radix_tree entry. In order to find the
+        * largest possible bit mask, a swap entry with swap type 0
         * and swap offset ~0UL is created, encoded to a swap pte,
-        * decoded to a swp_entry_t again and finally the swap
+        * decoded to a swp_entry_t again, and finally the swap
         * offset is extracted. This will mask all the bits from
         * the initial ~0UL mask that can't be encoded in either
         * the swp_entry_t or the architecture definition of a
-        * swap pte.
+        * swap pte.  Then the same is done for a radix_tree entry.
         */
        maxpages = swp_offset(pte_to_swp_entry(
-                       swp_entry_to_pte(swp_entry(0, ~0UL)))) + 1;
+                       swp_entry_to_pte(swp_entry(0, ~0UL))));
+       maxpages = swp_offset(radix_to_swp_entry(
+                       swp_to_radix_entry(swp_entry(0, maxpages)))) + 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 */
index 232eb2736a79624eeccfc1387e9360e78fdb0404..b40ac6d4e86e7ea7d1df670e709452948a1dcb74 100644 (file)
@@ -336,6 +336,14 @@ unsigned long invalidate_mapping_pages(struct address_space *mapping,
        unsigned long count = 0;
        int i;
 
+       /*
+        * Note: this function may get called on a shmem/tmpfs mapping:
+        * pagevec_lookup() might then return 0 prematurely (because it
+        * got a gangful of swap entries); but it's hardly worth worrying
+        * about - it can rarely have anything to free from such a mapping
+        * (most pages are dirty), and already skips over any difficulties.
+        */
+
        pagevec_init(&pvec, 0);
        while (index <= end && pagevec_lookup(&pvec, mapping, index,
                        min(end - index, (pgoff_t)PAGEVEC_SIZE - 1) + 1)) {
index 2252c2085dacd583d1894e2a5e47d010f82394e4..52cfd0c3ea71f19ef844531d055b4f96a0eb6e27 100644 (file)
@@ -242,8 +242,6 @@ static int br2684_xmit_vcc(struct sk_buff *skb, struct net_device *dev,
                if (brdev->payload == p_bridged) {
                        skb_push(skb, 2);
                        memset(skb->data, 0, 2);
-               } else { /* p_routed */
-                       skb_pull(skb, ETH_HLEN);
                }
        }
        skb_debug(skb);
index 2beda824636e7d270c8a8f24f2e084cfdef75ab4..27002dffe7ed3ea4df4f9b8d77b4210615d668c2 100644 (file)
@@ -1369,8 +1369,21 @@ pull_pages:
 }
 EXPORT_SYMBOL(__pskb_pull_tail);
 
-/* Copy some data bits from skb to kernel buffer. */
-
+/**
+ *     skb_copy_bits - copy bits from skb to kernel buffer
+ *     @skb: source skb
+ *     @offset: offset in source
+ *     @to: destination buffer
+ *     @len: number of bytes to copy
+ *
+ *     Copy the specified number of bytes from the source skb to the
+ *     destination buffer.
+ *
+ *     CAUTION ! :
+ *             If its prototype is ever changed,
+ *             check arch/{*}/net/{*}.S files,
+ *             since it is called from BPF assembly code.
+ */
 int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
 {
        int start = skb_headlen(skb);
index f1d27f6c9351d2a2bdc15d22fbb35ea270022f22..283c0a26e03f9ba2e3e0532638b6810fb4f7f349 100644 (file)
@@ -1718,7 +1718,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
 
                pmc->sfcount[sfmode]--;
                for (j=0; j<i; j++)
-                       (void) ip_mc_del1_src(pmc, sfmode, &psfsrc[i]);
+                       (void) ip_mc_del1_src(pmc, sfmode, &psfsrc[j]);
        } else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) {
 #ifdef CONFIG_IP_MULTICAST
                struct ip_sf_list *psf;
index ccaaa851ab429ee48b0aa803fd5bf8edd5ee34ce..77d3eded665ad2e30c03f8e62ea30b29ad1b4bb9 100644 (file)
@@ -204,9 +204,15 @@ static inline int ip_finish_output2(struct sk_buff *skb)
                skb = skb2;
        }
 
+       rcu_read_lock();
        neigh = dst_get_neighbour(dst);
-       if (neigh)
-               return neigh_output(neigh, skb);
+       if (neigh) {
+               int res = neigh_output(neigh, skb);
+
+               rcu_read_unlock();
+               return res;
+       }
+       rcu_read_unlock();
 
        if (net_ratelimit())
                printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!\n");
index 1730689f560e67cd3e2142834d805216134202c9..6afc4eb50591ae00e1d456b98e97ca073b9a8ca3 100644 (file)
@@ -1628,16 +1628,18 @@ static int check_peer_redir(struct dst_entry *dst, struct inet_peer *peer)
 {
        struct rtable *rt = (struct rtable *) dst;
        __be32 orig_gw = rt->rt_gateway;
-       struct neighbour *n;
+       struct neighbour *n, *old_n;
 
        dst_confirm(&rt->dst);
 
-       neigh_release(dst_get_neighbour(&rt->dst));
-       dst_set_neighbour(&rt->dst, NULL);
-
        rt->rt_gateway = peer->redirect_learned.a4;
-       rt_bind_neighbour(rt);
-       n = dst_get_neighbour(&rt->dst);
+
+       n = ipv4_neigh_lookup(&rt->dst, &rt->rt_gateway);
+       if (IS_ERR(n))
+               return PTR_ERR(n);
+       old_n = xchg(&rt->dst._neighbour, n);
+       if (old_n)
+               neigh_release(old_n);
        if (!n || !(n->nud_state & NUD_VALID)) {
                if (n)
                        neigh_event_send(n, NULL);
index a55500cc0b29ab710e012d42f0862a95f4750669..f012ebd87b4338181f76d3154165eb1f41cd0ba4 100644 (file)
@@ -656,7 +656,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
         * layer address of our nexhop router
         */
 
-       if (dst_get_neighbour(&rt->dst) == NULL)
+       if (dst_get_neighbour_raw(&rt->dst) == NULL)
                ifa->flags &= ~IFA_F_OPTIMISTIC;
 
        ifa->idev = idev;
index 16560336eb72315c9abb6e3938b6c01af3f3aa1f..9ef1831746efb70a1776791279e3c263f0be18d0 100644 (file)
 #include <linux/errqueue.h>
 #include <asm/uaccess.h>
 
+static inline int ipv6_mapped_addr_any(const struct in6_addr *a)
+{
+       return (ipv6_addr_v4mapped(a) && (a->s6_addr32[3] == 0));
+}
+
 int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
        struct sockaddr_in6     *usin = (struct sockaddr_in6 *) uaddr;
@@ -102,10 +107,12 @@ ipv4_connected:
 
                ipv6_addr_set_v4mapped(inet->inet_daddr, &np->daddr);
 
-               if (ipv6_addr_any(&np->saddr))
+               if (ipv6_addr_any(&np->saddr) ||
+                   ipv6_mapped_addr_any(&np->saddr))
                        ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr);
 
-               if (ipv6_addr_any(&np->rcv_saddr)) {
+               if (ipv6_addr_any(&np->rcv_saddr) ||
+                   ipv6_mapped_addr_any(&np->rcv_saddr)) {
                        ipv6_addr_set_v4mapped(inet->inet_rcv_saddr,
                                               &np->rcv_saddr);
                        if (sk->sk_prot->rehash)
index 54a4678955bf57b850a7a3a9fff70d0fed0eaf14..320d91d20ad7ddfd981cee92161482f218c225b4 100644 (file)
@@ -1455,7 +1455,7 @@ static int fib6_age(struct rt6_info *rt, void *arg)
                        RT6_TRACE("aging clone %p\n", rt);
                        return -1;
                } else if ((rt->rt6i_flags & RTF_GATEWAY) &&
-                          (!(dst_get_neighbour(&rt->dst)->flags & NTF_ROUTER))) {
+                          (!(dst_get_neighbour_raw(&rt->dst)->flags & NTF_ROUTER))) {
                        RT6_TRACE("purging route %p via non-router but gateway\n",
                                  rt);
                        return -1;
index 32e5339db0c899928b2b805e537ab06b8d0e498b..4c882cf4e8a14f21514e053fd0d125926e75584e 100644 (file)
@@ -135,10 +135,15 @@ static int ip6_finish_output2(struct sk_buff *skb)
                                skb->len);
        }
 
+       rcu_read_lock();
        neigh = dst_get_neighbour(dst);
-       if (neigh)
-               return neigh_output(neigh, skb);
+       if (neigh) {
+               int res = neigh_output(neigh, skb);
 
+               rcu_read_unlock();
+               return res;
+       }
+       rcu_read_unlock();
        IP6_INC_STATS_BH(dev_net(dst->dev),
                         ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
        kfree_skb(skb);
@@ -975,12 +980,14 @@ static int ip6_dst_lookup_tail(struct sock *sk,
         * dst entry and replace it instead with the
         * dst entry of the nexthop router
         */
+       rcu_read_lock();
        n = dst_get_neighbour(*dst);
        if (n && !(n->nud_state & NUD_VALID)) {
                struct inet6_ifaddr *ifp;
                struct flowi6 fl_gw6;
                int redirect;
 
+               rcu_read_unlock();
                ifp = ipv6_get_ifaddr(net, &fl6->saddr,
                                      (*dst)->dev, 1);
 
@@ -1000,6 +1007,8 @@ static int ip6_dst_lookup_tail(struct sock *sk,
                        if ((err = (*dst)->error))
                                goto out_err_release;
                }
+       } else {
+               rcu_read_unlock();
        }
 #endif
 
index e8987da06667225e44dae0cbfaf7fc51490788d7..9e69eb0ec6dd48a96d92bcf59c2cd89ad5e8412a 100644 (file)
@@ -364,7 +364,7 @@ out:
 #ifdef CONFIG_IPV6_ROUTER_PREF
 static void rt6_probe(struct rt6_info *rt)
 {
-       struct neighbour *neigh = rt ? dst_get_neighbour(&rt->dst) : NULL;
+       struct neighbour *neigh;
        /*
         * Okay, this does not seem to be appropriate
         * for now, however, we need to check if it
@@ -373,8 +373,10 @@ static void rt6_probe(struct rt6_info *rt)
         * Router Reachability Probe MUST be rate-limited
         * to no more than one per minute.
         */
+       rcu_read_lock();
+       neigh = rt ? dst_get_neighbour(&rt->dst) : NULL;
        if (!neigh || (neigh->nud_state & NUD_VALID))
-               return;
+               goto out;
        read_lock_bh(&neigh->lock);
        if (!(neigh->nud_state & NUD_VALID) &&
            time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
@@ -387,8 +389,11 @@ static void rt6_probe(struct rt6_info *rt)
                target = (struct in6_addr *)&neigh->primary_key;
                addrconf_addr_solict_mult(target, &mcaddr);
                ndisc_send_ns(rt->rt6i_dev, NULL, target, &mcaddr, NULL);
-       } else
+       } else {
                read_unlock_bh(&neigh->lock);
+       }
+out:
+       rcu_read_unlock();
 }
 #else
 static inline void rt6_probe(struct rt6_info *rt)
@@ -412,8 +417,11 @@ static inline int rt6_check_dev(struct rt6_info *rt, int oif)
 
 static inline int rt6_check_neigh(struct rt6_info *rt)
 {
-       struct neighbour *neigh = dst_get_neighbour(&rt->dst);
+       struct neighbour *neigh;
        int m;
+
+       rcu_read_lock();
+       neigh = dst_get_neighbour(&rt->dst);
        if (rt->rt6i_flags & RTF_NONEXTHOP ||
            !(rt->rt6i_flags & RTF_GATEWAY))
                m = 1;
@@ -430,6 +438,7 @@ static inline int rt6_check_neigh(struct rt6_info *rt)
                read_unlock_bh(&neigh->lock);
        } else
                m = 0;
+       rcu_read_unlock();
        return m;
 }
 
@@ -769,7 +778,7 @@ static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
                rt->rt6i_dst.plen = 128;
                rt->rt6i_flags |= RTF_CACHE;
                rt->dst.flags |= DST_HOST;
-               dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour(&ort->dst)));
+               dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_raw(&ort->dst)));
        }
        return rt;
 }
@@ -803,7 +812,7 @@ restart:
        dst_hold(&rt->dst);
        read_unlock_bh(&table->tb6_lock);
 
-       if (!dst_get_neighbour(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
+       if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
                nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
        else if (!(rt->dst.flags & DST_HOST))
                nrt = rt6_alloc_clone(rt, &fl6->daddr);
@@ -1587,7 +1596,7 @@ void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src,
        dst_confirm(&rt->dst);
 
        /* Duplicate redirect: silently ignore. */
-       if (neigh == dst_get_neighbour(&rt->dst))
+       if (neigh == dst_get_neighbour_raw(&rt->dst))
                goto out;
 
        nrt = ip6_rt_copy(rt, dest);
@@ -1682,7 +1691,7 @@ again:
           1. It is connected route. Action: COW
           2. It is gatewayed route or NONEXTHOP route. Action: clone it.
         */
-       if (!dst_get_neighbour(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
+       if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
                nrt = rt6_alloc_cow(rt, daddr, saddr);
        else
                nrt = rt6_alloc_clone(rt, daddr);
@@ -2326,6 +2335,7 @@ static int rt6_fill_node(struct net *net,
        struct nlmsghdr *nlh;
        long expires;
        u32 table;
+       struct neighbour *n;
 
        if (prefix) {   /* user wants prefix routes only */
                if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
@@ -2414,8 +2424,11 @@ static int rt6_fill_node(struct net *net,
        if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
                goto nla_put_failure;
 
-       if (dst_get_neighbour(&rt->dst))
-               NLA_PUT(skb, RTA_GATEWAY, 16, &dst_get_neighbour(&rt->dst)->primary_key);
+       rcu_read_lock();
+       n = dst_get_neighbour(&rt->dst);
+       if (n)
+               NLA_PUT(skb, RTA_GATEWAY, 16, &n->primary_key);
+       rcu_read_unlock();
 
        if (rt->dst.dev)
                NLA_PUT_U32(skb, RTA_OIF, rt->rt6i_dev->ifindex);
@@ -2608,12 +2621,14 @@ static int rt6_info_route(struct rt6_info *rt, void *p_arg)
 #else
        seq_puts(m, "00000000000000000000000000000000 00 ");
 #endif
+       rcu_read_lock();
        n = dst_get_neighbour(&rt->dst);
        if (n) {
                seq_printf(m, "%pi6", n->primary_key);
        } else {
                seq_puts(m, "00000000000000000000000000000000");
        }
+       rcu_read_unlock();
        seq_printf(m, " %08x %08x %08x %08x %8s\n",
                   rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
                   rt->dst.__use, rt->rt6i_flags,
index be43fd805bd073c4f17bb160b57d673626220a1b..2b771dc708a30d212ed37fc1a76abb759279f159 100644 (file)
@@ -3771,6 +3771,7 @@ err_sock:
 void ip_vs_control_cleanup(void)
 {
        EnterFunction(2);
+       unregister_netdevice_notifier(&ip_vs_dst_notifier);
        ip_vs_genl_unregister();
        nf_unregister_sockopt(&ip_vs_sockopts);
        LeaveFunction(2);
index ea750e9df65f7bdbfafcd6ae4246d6d9520707ee..d2732fc952e2adbe224c46cb827fdcd4b38ffbaf 100644 (file)
@@ -1,8 +1,6 @@
 #
 # Makefile for the NetLabel subsystem.
 #
-# Feb 9, 2006, Paul Moore <paul.moore@hp.com>
-#
 
 # base objects
 obj-y  := netlabel_user.o netlabel_kapi.o
index c0519139679e4ccfc46bd065915a9486d2a83d10..96b749dacc344def55e226188a5095b74a3f1b40 100644 (file)
@@ -6,7 +6,7 @@
  * system manages static and dynamic label mappings for network protocols such
  * as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index 2b9644e19de0dc8f1d225b19e834403613d40e42..fdbc1d2c7352be7f993f814ef205cdbd9c5dec2e 100644 (file)
@@ -6,7 +6,7 @@
  * system manages static and dynamic label mappings for network protocols such
  * as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index dd53a36d89af2f5d3a538d65c416eb45eb5deee9..6bf878335d9436d40b1619c37c8918f3bd12beb0 100644 (file)
@@ -5,7 +5,7 @@
  * NetLabel system manages static and dynamic label mappings for network
  * protocols such as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index af7f3355103e25ace814afdb08e9134620e8ce5e..d24d774bfd62c065a5be92fb4e5e97cbb275e3ce 100644 (file)
@@ -5,7 +5,7 @@
  * NetLabel system manages static and dynamic label mappings for network
  * protocols such as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index 2aa975e5452d90ca3a3758c5b8f4a5d5b9357c56..7d8083cde34f4019d6f1d0e10835a9d87cc69b03 100644 (file)
@@ -6,7 +6,7 @@
  * system manages static and dynamic label mappings for network protocols such
  * as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index 0261dda3f2d2588795be79064dec6bade3e9ec06..bfcc0f7024c5d69547ade4bb7cb76f58f9e1564d 100644 (file)
@@ -6,7 +6,7 @@
  * system manages static and dynamic label mappings for network protocols such
  * as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index b528dd928d3c2fd580ace2d5d10cf947ab764b19..58107d06084614c6281a48f1d4c835038eed323e 100644 (file)
@@ -5,7 +5,7 @@
  * system manages static and dynamic label mappings for network protocols such
  * as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index dff8a08092459690b1ebb47d667001b3dd3af4bc..bfa55586977572e668d2a53004513358ce9efcf0 100644 (file)
@@ -5,7 +5,7 @@
  * NetLabel system manages static and dynamic label mappings for network
  * protocols such as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index 8db37f4c10f7fcaa89dc778c4b49e8fd16d2f346..5a9f31ce5799baef8b6e83c4d56222cff7603d2b 100644 (file)
@@ -5,7 +5,7 @@
  * NetLabel system manages static and dynamic label mappings for network
  * protocols such as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index f1ecf848e3acb69db72ae2cc2efdd8f6a74aaac8..e6e823656f9dd1cdba6b37a846e52563cb6828d9 100644 (file)
@@ -5,7 +5,7 @@
  * NetLabel system.  The NetLabel system manages static and dynamic label
  * mappings for network protocols such as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index 0bc8dc3f9e3cbdb74c8d846fc919904ada484b6f..700af49022a040a2d11a23747e51e1a0b6b9eca4 100644 (file)
@@ -5,7 +5,7 @@
  * NetLabel system.  The NetLabel system manages static and dynamic label
  * mappings for network protocols such as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index a3fd75ac3fa50187bd7ff6924700569b2b1cebc4..9fae63f102980e2e789c2d8b06baf9594f1cb4b6 100644 (file)
@@ -5,7 +5,7 @@
  * NetLabel system manages static and dynamic label mappings for network
  * protocols such as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index f4fc4c9ad5670340f356bd854a89db1f93416e3f..81969785e279e6b47c5f50f391ed8bd371ad04e3 100644 (file)
@@ -5,7 +5,7 @@
  * NetLabel system manages static and dynamic label mappings for network
  * protocols such as CIPSO and RIPSO.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index 4536ee64383e8a6a02a076374ed96b5036df9bd6..4f5510e2bd6f659e85f726a239bb902d96e3c35d 100644 (file)
@@ -410,7 +410,12 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
        /* Return Congestion Notification only if we dropped a packet
         * from this flow.
         */
-       return (qlen != slot->qlen) ? NET_XMIT_CN : NET_XMIT_SUCCESS;
+       if (qlen != slot->qlen)
+               return NET_XMIT_CN;
+
+       /* As we dropped a packet, better let upper stack know this */
+       qdisc_tree_decrease_qlen(sch, 1);
+       return NET_XMIT_SUCCESS;
 }
 
 static struct sk_buff *
index b1cbbcd9255854a7d52571688160bf3d87baf483..24a77400b65e4e8fd63f825144bdc4891b5fced9 100644 (file)
@@ -1871,8 +1871,14 @@ SYSCALL_DEFINE2(shutdown, int, fd, int, how)
 #define COMPAT_NAMELEN(msg)    COMPAT_MSG(msg, msg_namelen)
 #define COMPAT_FLAGS(msg)      COMPAT_MSG(msg, msg_flags)
 
+struct used_address {
+       struct sockaddr_storage name;
+       unsigned int name_len;
+};
+
 static int __sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
-                        struct msghdr *msg_sys, unsigned flags, int nosec)
+                        struct msghdr *msg_sys, unsigned flags,
+                        struct used_address *used_address)
 {
        struct compat_msghdr __user *msg_compat =
            (struct compat_msghdr __user *)msg;
@@ -1953,8 +1959,28 @@ static int __sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
 
        if (sock->file->f_flags & O_NONBLOCK)
                msg_sys->msg_flags |= MSG_DONTWAIT;
-       err = (nosec ? sock_sendmsg_nosec : sock_sendmsg)(sock, msg_sys,
-                                                         total_len);
+       /*
+        * If this is sendmmsg() and current destination address is same as
+        * previously succeeded address, omit asking LSM's decision.
+        * used_address->name_len is initialized to UINT_MAX so that the first
+        * destination address never matches.
+        */
+       if (used_address && used_address->name_len == msg_sys->msg_namelen &&
+           !memcmp(&used_address->name, msg->msg_name,
+                   used_address->name_len)) {
+               err = sock_sendmsg_nosec(sock, msg_sys, total_len);
+               goto out_freectl;
+       }
+       err = sock_sendmsg(sock, msg_sys, total_len);
+       /*
+        * If this is sendmmsg() and sending to current destination address was
+        * successful, remember it.
+        */
+       if (used_address && err >= 0) {
+               used_address->name_len = msg_sys->msg_namelen;
+               memcpy(&used_address->name, msg->msg_name,
+                      used_address->name_len);
+       }
 
 out_freectl:
        if (ctl_buf != ctl)
@@ -1979,7 +2005,7 @@ SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned, flags)
        if (!sock)
                goto out;
 
-       err = __sys_sendmsg(sock, msg, &msg_sys, flags, 0);
+       err = __sys_sendmsg(sock, msg, &msg_sys, flags, NULL);
 
        fput_light(sock->file, fput_needed);
 out:
@@ -1998,6 +2024,10 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
        struct mmsghdr __user *entry;
        struct compat_mmsghdr __user *compat_entry;
        struct msghdr msg_sys;
+       struct used_address used_address;
+
+       if (vlen > UIO_MAXIOV)
+               vlen = UIO_MAXIOV;
 
        datagrams = 0;
 
@@ -2005,27 +2035,22 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
        if (!sock)
                return err;
 
-       err = sock_error(sock->sk);
-       if (err)
-               goto out_put;
-
+       used_address.name_len = UINT_MAX;
        entry = mmsg;
        compat_entry = (struct compat_mmsghdr __user *)mmsg;
+       err = 0;
 
        while (datagrams < vlen) {
-               /*
-                * No need to ask LSM for more than the first datagram.
-                */
                if (MSG_CMSG_COMPAT & flags) {
                        err = __sys_sendmsg(sock, (struct msghdr __user *)compat_entry,
-                                           &msg_sys, flags, datagrams);
+                                           &msg_sys, flags, &used_address);
                        if (err < 0)
                                break;
                        err = __put_user(err, &compat_entry->msg_len);
                        ++compat_entry;
                } else {
                        err = __sys_sendmsg(sock, (struct msghdr __user *)entry,
-                                           &msg_sys, flags, datagrams);
+                                           &msg_sys, flags, &used_address);
                        if (err < 0)
                                break;
                        err = put_user(err, &entry->msg_len);
@@ -2037,29 +2062,11 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
                ++datagrams;
        }
 
-out_put:
        fput_light(sock->file, fput_needed);
 
-       if (err == 0)
-               return datagrams;
-
-       if (datagrams != 0) {
-               /*
-                * We may send less entries than requested (vlen) if the
-                * sock is non blocking...
-                */
-               if (err != -EAGAIN) {
-                       /*
-                        * ... or if sendmsg returns an error after we
-                        * send some datagrams, where we record the
-                        * error to return on the next call or if the
-                        * app asks about it using getsockopt(SO_ERROR).
-                        */
-                       sock->sk->sk_err = -err;
-               }
-
+       /* We only return an error if no datagrams were able to be sent */
+       if (datagrams != 0)
                return datagrams;
-       }
 
        return err;
 }
index 9b6a4d1ea8f800def10f4db686c71d6cf4d6c563..f4385e45a5fcb398086c6c8c5c8ab4daff7ee139 100644 (file)
@@ -187,6 +187,7 @@ EXPORT_SYMBOL_GPL(xprt_load_transport);
 /**
  * xprt_reserve_xprt - serialize write access to transports
  * @task: task that is requesting access to the transport
+ * @xprt: pointer to the target transport
  *
  * This prevents mixing the payload of separate requests, and prevents
  * transport connects from colliding with writes.  No congestion control
index 28d2aa109beecbaa8d5ef97890142a16e134cbad..e83e7fee3bc032def174d7741e6e419d693d2401 100644 (file)
@@ -3464,7 +3464,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                                    tmp) {
                        enum ieee80211_band band = nla_type(attr);
 
-                       if (band < 0 || band > IEEE80211_NUM_BANDS) {
+                       if (band < 0 || band >= IEEE80211_NUM_BANDS) {
                                err = -EINVAL;
                                goto out_free;
                        }
index 58064d9e565d80cc4fe542e9dec93f262cab8b7a..791ab2e77f3f2e6ec2f190da781982cd3fc6ffbe 100644 (file)
@@ -462,8 +462,8 @@ static struct xfrm_algo_desc ealg_list[] = {
        .desc = {
                .sadb_alg_id = SADB_X_EALG_AESCTR,
                .sadb_alg_ivlen = 8,
-               .sadb_alg_minbits = 128,
-               .sadb_alg_maxbits = 256
+               .sadb_alg_minbits = 160,
+               .sadb_alg_maxbits = 288
        }
 },
 };
index a38316b2e3f66b9840debbf830d57d6e111cb2e1..266a2292451dcd247410fc3f0ffe8d12d82d8ad3 100644 (file)
@@ -14,7 +14,7 @@
  *  Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
  *                         <dgoeddel@trustedcs.com>
  *  Copyright (C) 2006, 2007, 2009 Hewlett-Packard Development Company, L.P.
- *     Paul Moore <paul.moore@hp.com>
+ *     Paul Moore <paul@paul-moore.com>
  *  Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
  *                    Yuichi Nakamura <ynakam@hitachisoft.jp>
  *
index ce23edd128b3893b7649c374ea52ee7a7a8bfc29..43d507242b42f1ecdbe0f2971074d5d7b80165c5 100644 (file)
@@ -8,7 +8,7 @@
  *
  * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
  * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
- *                    Paul Moore, <paul.moore@hp.com>
+ *                    Paul Moore <paul@paul-moore.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,
index cf2f628e6e28a0a12f1ec2185b88ea918ce430e4..8c59b8f150e888edf09195dedd1c687f9b0784fe 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * SELinux interface to the NetLabel subsystem
  *
- * Author : Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index 1b94450d11d276458380b0fbdfa5731bdd5be428..df7a5ed6c6943e01008b010acb9782c2b727d824 100644 (file)
@@ -6,7 +6,7 @@
  * needed to reduce the lookup overhead since most of these queries happen on
  * a per-packet basis.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index 8991752eaf93faf9ac1adeef0edca1336b09278d..4d965b83d735b976029231274c2ace509b498e1f 100644 (file)
@@ -5,7 +5,7 @@
  * mapping is maintained as part of the normal policy but a fast cache is
  * needed to reduce the lookup overhead.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index 58cc481c93d5e580e554cd9b3b61bc9214128193..326f22cbe405d745912965a11bd7c6babb28e7b6 100644 (file)
@@ -8,7 +8,7 @@
  *
  * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
  * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
- *                   Paul Moore <paul.moore@hp.com>
+ *                   Paul Moore <paul@paul-moore.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,
index c3bf3ed07b06275b6e71f3f07177e327b42e9e5b..da4b8b2332802c9624f2f7f49ea8d622f96e180a 100644 (file)
@@ -4,7 +4,7 @@
  * This file provides the necessary glue to tie NetLabel into the SELinux
  * subsystem.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  */
 
index 8b691a8631866905e441c78ac26f569b74840290..3bf46abaa688cbb7735f1d47cf3d33348f9b019a 100644 (file)
@@ -6,7 +6,7 @@
  * needed to reduce the lookup overhead since most of these queries happen on
  * a per-packet basis.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  * This code is heavily based on the "netif" concept originally developed by
  * James Morris <jmorris@redhat.com>
index ae76e298de7dfd30f951f1a789b6dea222747d44..0b62bd112461c712cfc9425f9e8ac0f6caf194f0 100644 (file)
@@ -5,7 +5,7 @@
  * mapping is maintained as part of the normal policy but a fast cache is
  * needed to reduce the lookup overhead.
  *
- * Author: Paul Moore <paul.moore@hp.com>
+ * Author: Paul Moore <paul@paul-moore.com>
  *
  * This code is heavily based on the "netif" concept originally developed by
  * James Morris <jmorris@redhat.com>
index de7900ef53da6257ab610629fd8ef65368526d62..55d92cbb177acaf31a7b84282cf8d56b0c71fbe5 100644 (file)
@@ -2,7 +2,7 @@
  *
  *     Added conditional policy language extensions
  *
- *  Updated: Hewlett-Packard <paul.moore@hp.com>
+ *  Updated: Hewlett-Packard <paul@paul-moore.com>
  *
  *     Added support for the policy capability bitmap
  *
index d42951fcbe877355b08c16d5a63526c729f4355c..30f119b1d1ec36a95dc456c52b5b0ac1a6868514 100644 (file)
@@ -4,7 +4,7 @@
  * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
  */
 /*
- * Updated: Hewlett-Packard <paul.moore@hp.com>
+ * Updated: Hewlett-Packard <paul@paul-moore.com>
  *
  *      Added support to import/export the NetLabel category bitmap
  *
index e96174216bc9ddb03313eb2dfca363fcdced207a..fbf9c5816c716a0d9898d7ae5dd91ae6f12575a2 100644 (file)
@@ -11,7 +11,7 @@
  * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
  */
 /*
- * Updated: Hewlett-Packard <paul.moore@hp.com>
+ * Updated: Hewlett-Packard <paul@paul-moore.com>
  *
  *      Added support to import/export the MLS label from NetLabel
  *
index 037bf9d82d41d9c67c32d3d602751aa626e508b5..e4369e3e6366f89e52690e64ad16ae00ba45b985 100644 (file)
@@ -11,7 +11,7 @@
  * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
  */
 /*
- * Updated: Hewlett-Packard <paul.moore@hp.com>
+ * Updated: Hewlett-Packard <paul@paul-moore.com>
  *
  *     Added support to import/export the MLS label from NetLabel
  *
index d246aca3f4fbd43563bf01d20d78e28feb826ad8..2381d0ded228d492a207b2745a6bc59269114316 100644 (file)
@@ -13,7 +13,7 @@
  *
  *     Added conditional policy language extensions
  *
- * Updated: Hewlett-Packard <paul.moore@hp.com>
+ * Updated: Hewlett-Packard <paul@paul-moore.com>
  *
  *      Added support for the policy capability bitmap
  *
index 973e00e34fa9f18ff95d7920a18edbeb5987a5ba..f6917bc0aa05322716d8fd5a4e0a46effd3be45c 100644 (file)
@@ -13,7 +13,7 @@
  *
  *     Added conditional policy language extensions
  *
- * Updated: Hewlett-Packard <paul.moore@hp.com>
+ * Updated: Hewlett-Packard <paul@paul-moore.com>
  *
  *      Added support for NetLabel
  *      Added support for the policy capability bitmap
index f375eb2e19576dfb20e9c75f8847edb3b92d89d0..b9c5e149903b65706c848f436862971ccf80bab5 100644 (file)
@@ -9,7 +9,7 @@
  *
  *  Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
  *  Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
- *                Paul Moore <paul.moore@hp.com>
+ *                Paul Moore <paul@paul-moore.com>
  *  Copyright (C) 2010 Nokia Corporation
  *
  *     This program is free software; you can redistribute it and/or modify
index 5fb2e28e796f6a2766146dbdc5234a1de9a71680..91cdf9435feca986cf3be980412b7d21a9dddecd 100644 (file)
@@ -342,7 +342,7 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
                        kfree(bufs);
                        return -EFAULT;
                }
-               bufs[ch] = compat_ptr(ptr);
+               bufs[i] = compat_ptr(ptr);
                bufptr++;
        }
        if (dir == SNDRV_PCM_STREAM_PLAYBACK)
index 0851cd13e30394811e54fb29ed9a02112c9849ac..e85e72baff9e8535c9cbd7a56ba0e4d65ddd1ac1 100644 (file)
@@ -22,7 +22,7 @@
 
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
 #include <linux/log2.h>
 #include <sound/core.h>
 #include <sound/timer.h>
index 3a7afa31c1d8c461458d5793c25da6be415b92e5..71d32c868c9244e19160687922fb7c7fa86ca97c 100644 (file)
@@ -43,6 +43,7 @@ short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code,
        struct pci_dev *dev = os_data;
        struct code_header header;
        char fw_name[20];
+       short err_ret = HPI_ERROR_DSP_FILE_NOT_FOUND;
        int err;
 
        sprintf(fw_name, "asihpi/dsp%04x.bin", adapter);
@@ -85,8 +86,10 @@ short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code,
 
        HPI_DEBUG_LOG(DEBUG, "dsp code %s opened\n", fw_name);
        dsp_code->pvt = kmalloc(sizeof(*dsp_code->pvt), GFP_KERNEL);
-       if (!dsp_code->pvt)
-               return HPI_ERROR_MEMORY_ALLOC;
+       if (!dsp_code->pvt) {
+               err_ret = HPI_ERROR_MEMORY_ALLOC;
+               goto error2;
+       }
 
        dsp_code->pvt->dev = dev;
        dsp_code->pvt->firmware = firmware;
@@ -99,7 +102,7 @@ error2:
        release_firmware(firmware);
 error1:
        dsp_code->block_length = 0;
-       return HPI_ERROR_DSP_FILE_NOT_FOUND;
+       return err_ret;
 }
 
 /*-------------------------------------------------------------------*/
index 9683f84ecdc835ea964932e358b4a66bcbc6a628..a32502e796de937946b1e1b2c9b9d3318441049f 100644 (file)
@@ -177,16 +177,21 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        } else {
                u16 __user *ptr = NULL;
                u32 size = 0;
-
+               u32 adapter_present;
                /* -1=no data 0=read from user mem, 1=write to user mem */
                int wrflag = -1;
-               u32 adapter = hm->h.adapter_index;
-               struct hpi_adapter *pa = &adapters[adapter];
+               struct hpi_adapter *pa;
+
+               if (hm->h.adapter_index < HPI_MAX_ADAPTERS) {
+                       pa = &adapters[hm->h.adapter_index];
+                       adapter_present = pa->type;
+               } else {
+                       adapter_present = 0;
+               }
 
-               if ((adapter >= HPI_MAX_ADAPTERS) || (!pa->type)) {
-                       hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
-                               HPI_ADAPTER_OPEN,
-                               HPI_ERROR_BAD_ADAPTER_NUMBER);
+               if (!adapter_present) {
+                       hpi_init_response(&hr->r0, hm->h.object,
+                               hm->h.function, HPI_ERROR_BAD_ADAPTER_NUMBER);
 
                        uncopied_bytes =
                                copy_to_user(puhr, hr, sizeof(hr->h));
index af130ee0c45d9dda8d294e5793c6c67b72be5f24..6edc67ced905f4815c3b021f15c75d3e7cd1bb92 100644 (file)
@@ -521,6 +521,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 #define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024)
 
 /* revisions >= 230 indicate AES32 card */
+#define HDSPM_MADI_ANCIENT_REV 204
 #define HDSPM_MADI_OLD_REV     207
 #define HDSPM_MADI_REV         210
 #define HDSPM_RAYDAT_REV       211
@@ -1217,6 +1218,22 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
                                rate = 0;
                                break;
                        }
+
+                       /* QS and DS rates normally can not be detected
+                        * automatically by the card. Only exception is MADI
+                        * in 96k frame mode.
+                        *
+                        * So if we read SS values (32 .. 48k), check for
+                        * user-provided DS/QS bits in the control register
+                        * and multiply the base frequency accordingly.
+                        */
+                       if (rate <= 48000) {
+                               if (hdspm->control_register & HDSPM_QuadSpeed)
+                                       rate *= 4;
+                               else if (hdspm->control_register &
+                                               HDSPM_DoubleSpeed)
+                                       rate *= 2;
+                       }
                }
                break;
        }
@@ -3415,6 +3432,91 @@ static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+#define HDSPM_MADI_SPEEDMODE(xname, xindex) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .index = xindex, \
+       .info = snd_hdspm_info_madi_speedmode, \
+       .get = snd_hdspm_get_madi_speedmode, \
+       .put = snd_hdspm_put_madi_speedmode \
+}
+
+static int hdspm_madi_speedmode(struct hdspm *hdspm)
+{
+       if (hdspm->control_register & HDSPM_QuadSpeed)
+               return 2;
+       if (hdspm->control_register & HDSPM_DoubleSpeed)
+               return 1;
+       return 0;
+}
+
+static int hdspm_set_madi_speedmode(struct hdspm *hdspm, int mode)
+{
+       hdspm->control_register &= ~(HDSPM_DoubleSpeed | HDSPM_QuadSpeed);
+       switch (mode) {
+       case 0:
+               break;
+       case 1:
+               hdspm->control_register |= HDSPM_DoubleSpeed;
+               break;
+       case 2:
+               hdspm->control_register |= HDSPM_QuadSpeed;
+               break;
+       }
+       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+       return 0;
+}
+
+static int snd_hdspm_info_madi_speedmode(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[] = { "Single", "Double", "Quad" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 3;
+
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                   uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+
+       return 0;
+}
+
+static int snd_hdspm_get_madi_speedmode(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+       spin_lock_irq(&hdspm->lock);
+       ucontrol->value.enumerated.item[0] = hdspm_madi_speedmode(hdspm);
+       spin_unlock_irq(&hdspm->lock);
+       return 0;
+}
+
+static int snd_hdspm_put_madi_speedmode(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+       int change;
+       int val;
+
+       if (!snd_hdspm_use_is_exclusive(hdspm))
+               return -EBUSY;
+       val = ucontrol->value.integer.value[0];
+       if (val < 0)
+               val = 0;
+       if (val > 2)
+               val = 2;
+       spin_lock_irq(&hdspm->lock);
+       change = val != hdspm_madi_speedmode(hdspm);
+       hdspm_set_madi_speedmode(hdspm, val);
+       spin_unlock_irq(&hdspm->lock);
+       return change;
+}
 
 #define HDSPM_MIXER(xname, xindex) \
 { .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
@@ -4289,7 +4391,8 @@ static struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
        HDSPM_TX_64("TX 64 channels mode", 0),
        HDSPM_C_TMS("Clear Track Marker", 0),
        HDSPM_SAFE_MODE("Safe Mode", 0),
-       HDSPM_INPUT_SELECT("Input Select", 0)
+       HDSPM_INPUT_SELECT("Input Select", 0),
+       HDSPM_MADI_SPEEDMODE("MADI Speed Mode", 0)
 };
 
 
@@ -4302,7 +4405,8 @@ static struct snd_kcontrol_new snd_hdspm_controls_madiface[] = {
        HDSPM_SYNC_CHECK("MADI SyncCheck", 0),
        HDSPM_TX_64("TX 64 channels mode", 0),
        HDSPM_C_TMS("Clear Track Marker", 0),
-       HDSPM_SAFE_MODE("Safe Mode", 0)
+       HDSPM_SAFE_MODE("Safe Mode", 0),
+       HDSPM_MADI_SPEEDMODE("MADI Speed Mode", 0)
 };
 
 static struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
@@ -6381,6 +6485,7 @@ static int __devinit snd_hdspm_create(struct snd_card *card,
        switch (hdspm->firmware_rev) {
        case HDSPM_MADI_REV:
        case HDSPM_MADI_OLD_REV:
+       case HDSPM_MADI_ANCIENT_REV:
                hdspm->io_type = MADI;
                hdspm->card_name = "RME MADI";
                hdspm->midiPorts = 3;
index 34aa972669eddd845bad0d1a4a0ba596c57234c4..3de99af8cb82ccf6def128632603825e57c9ab77 100644 (file)
@@ -290,6 +290,7 @@ static void txx9aclc_pcm_free_dma_buffers(struct snd_pcm *pcm)
 
 static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
        struct snd_soc_dai *dai = rtd->cpu_dai;
        struct snd_pcm *pcm = rtd->pcm;
        struct platform_device *pdev = to_platform_device(dai->platform->dev);
index 6d8ef4a3a9b5bd017a805a86ad2e3fd5737ca8e0..8b2d37b59c9e2178c21256b240b3ca76b3081a76 100644 (file)
@@ -128,34 +128,34 @@ unsigned long long get_msr(int cpu, off_t offset)
 void print_header(void)
 {
        if (show_pkg)
-               fprintf(stderr, "pk");
+               fprintf(stderr, "pk");
        if (show_core)
-               fprintf(stderr, "core");
+               fprintf(stderr, " cr");
        if (show_cpu)
                fprintf(stderr, " CPU");
        if (do_nhm_cstates)
-               fprintf(stderr, "   %%c0 ");
+               fprintf(stderr, "    %%c0 ");
        if (has_aperf)
-               fprintf(stderr, "  GHz");
+               fprintf(stderr, " GHz");
        fprintf(stderr, "  TSC");
        if (do_nhm_cstates)
-               fprintf(stderr, "   %%c1 ");
+               fprintf(stderr, "    %%c1");
        if (do_nhm_cstates)
-               fprintf(stderr, "   %%c3 ");
+               fprintf(stderr, "    %%c3");
        if (do_nhm_cstates)
-               fprintf(stderr, "   %%c6 ");
+               fprintf(stderr, "    %%c6");
        if (do_snb_cstates)
-               fprintf(stderr, "   %%c7 ");
+               fprintf(stderr, "    %%c7");
        if (do_snb_cstates)
-               fprintf(stderr, "  %%pc2 ");
+               fprintf(stderr, "  %%pc2");
        if (do_nhm_cstates)
-               fprintf(stderr, "  %%pc3 ");
+               fprintf(stderr, "  %%pc3");
        if (do_nhm_cstates)
-               fprintf(stderr, "  %%pc6 ");
+               fprintf(stderr, "  %%pc6");
        if (do_snb_cstates)
-               fprintf(stderr, "  %%pc7 ");
+               fprintf(stderr, "  %%pc7");
        if (extra_msr_offset)
-               fprintf(stderr, "       MSR 0x%x ", extra_msr_offset);
+               fprintf(stderr, "        MSR 0x%x ", extra_msr_offset);
 
        putc('\n', stderr);
 }
@@ -194,14 +194,14 @@ void print_cnt(struct counters *p)
        /* topology columns, print blanks on 1st (average) line */
        if (p == cnt_average) {
                if (show_pkg)
-                       fprintf(stderr, "    ");
+                       fprintf(stderr, " ");
                if (show_core)
                        fprintf(stderr, "    ");
                if (show_cpu)
                        fprintf(stderr, "    ");
        } else {
                if (show_pkg)
-                       fprintf(stderr, "%4d", p->pkg);
+                       fprintf(stderr, "%d", p->pkg);
                if (show_core)
                        fprintf(stderr, "%4d", p->core);
                if (show_cpu)
@@ -241,22 +241,22 @@ void print_cnt(struct counters *p)
                if (!skip_c1)
                        fprintf(stderr, "%7.2f", 100.0 * p->c1/p->tsc);
                else
-                       fprintf(stderr, "   ****");
+                       fprintf(stderr, "  ****");
        }
        if (do_nhm_cstates)
-               fprintf(stderr, "%7.2f", 100.0 * p->c3/p->tsc);
+               fprintf(stderr, " %6.2f", 100.0 * p->c3/p->tsc);
        if (do_nhm_cstates)
-               fprintf(stderr, "%7.2f", 100.0 * p->c6/p->tsc);
+               fprintf(stderr, " %6.2f", 100.0 * p->c6/p->tsc);
        if (do_snb_cstates)
-               fprintf(stderr, "%7.2f", 100.0 * p->c7/p->tsc);
+               fprintf(stderr, " %6.2f", 100.0 * p->c7/p->tsc);
        if (do_snb_cstates)
-               fprintf(stderr, "%7.2f", 100.0 * p->pc2/p->tsc);
+               fprintf(stderr, " %5.2f", 100.0 * p->pc2/p->tsc);
        if (do_nhm_cstates)
-               fprintf(stderr, "%7.2f", 100.0 * p->pc3/p->tsc);
+               fprintf(stderr, " %5.2f", 100.0 * p->pc3/p->tsc);
        if (do_nhm_cstates)
-               fprintf(stderr, "%7.2f", 100.0 * p->pc6/p->tsc);
+               fprintf(stderr, " %5.2f", 100.0 * p->pc6/p->tsc);
        if (do_snb_cstates)
-               fprintf(stderr, "%7.2f", 100.0 * p->pc7/p->tsc);
+               fprintf(stderr, " %5.2f", 100.0 * p->pc7/p->tsc);
        if (extra_msr_offset)
                fprintf(stderr, "  0x%016llx", p->extra_msr);
        putc('\n', stderr);
index 2618ef2ba31fa6af37594202752079f3cee94b4a..33c5c7ee148f27b7c1a0b61c4b256aae6122d637 100644 (file)
@@ -137,7 +137,6 @@ void cmdline(int argc, char **argv)
 void validate_cpuid(void)
 {
        unsigned int eax, ebx, ecx, edx, max_level;
-       char brand[16];
        unsigned int fms, family, model, stepping;
 
        eax = ebx = ecx = edx = 0;
@@ -160,8 +159,8 @@ void validate_cpuid(void)
                model += ((fms >> 16) & 0xf) << 4;
 
        if (verbose > 1)
-               printf("CPUID %s %d levels family:model:stepping "
-                       "0x%x:%x:%x (%d:%d:%d)\n", brand, max_level,
+               printf("CPUID %d levels family:model:stepping "
+                       "0x%x:%x:%x (%d:%d:%d)\n", max_level,
                        family, model, stepping, family, model, stepping);
 
        if (!(edx & (1 << 5))) {