]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge Paulus' tree
authorStephen Rothwell <sfr@canb.auug.org.au>
Wed, 2 Nov 2005 07:15:43 +0000 (18:15 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Wed, 2 Nov 2005 07:15:43 +0000 (18:15 +1100)
252 files changed:
Documentation/filesystems/ntfs.txt
arch/arm/common/locomo.c
arch/arm/common/sa1111.c
arch/arm/common/scoop.c
arch/arm/mach-aaec2000/core.c
arch/arm/mach-h720x/h7202-eval.c
arch/arm/mach-imx/generic.c
arch/arm/mach-imx/mx1ads.c
arch/arm/mach-integrator/integrator_ap.c
arch/arm/mach-integrator/integrator_cp.c
arch/arm/mach-iop3xx/iop321-setup.c
arch/arm/mach-iop3xx/iop331-setup.c
arch/arm/mach-ixp2000/enp2611.c
arch/arm/mach-ixp2000/ixdp2x00.c
arch/arm/mach-ixp2000/ixdp2x01.c
arch/arm/mach-ixp4xx/common.c
arch/arm/mach-lh7a40x/arch-lpd7a40x.c
arch/arm/mach-omap1/board-h2.c
arch/arm/mach-omap1/board-h3.c
arch/arm/mach-omap1/board-innovator.c
arch/arm/mach-omap1/board-netstar.c
arch/arm/mach-omap1/board-osk.c
arch/arm/mach-omap1/board-perseus2.c
arch/arm/mach-omap1/board-voiceblue.c
arch/arm/mach-omap1/devices.c
arch/arm/mach-pxa/corgi.c
arch/arm/mach-pxa/corgi_lcd.c
arch/arm/mach-pxa/corgi_ssp.c
arch/arm/mach-pxa/generic.c
arch/arm/mach-pxa/idp.c
arch/arm/mach-pxa/lubbock.c
arch/arm/mach-pxa/mainstone.c
arch/arm/mach-pxa/poodle.c
arch/arm/mach-pxa/pxa27x.c
arch/arm/mach-pxa/spitz.c
arch/arm/mach-s3c2410/clock.c
arch/arm/mach-s3c2410/cpu.c
arch/arm/mach-s3c2410/devs.c
arch/arm/mach-s3c2410/devs.h
arch/arm/mach-s3c2410/mach-anubis.c
arch/arm/mach-s3c2410/mach-bast.c
arch/arm/mach-s3c2410/mach-h1940.c
arch/arm/mach-s3c2410/mach-n30.c
arch/arm/mach-s3c2410/mach-nexcoder.c
arch/arm/mach-s3c2410/mach-otom.c
arch/arm/mach-s3c2410/mach-rx3715.c
arch/arm/mach-s3c2410/mach-smdk2410.c
arch/arm/mach-s3c2410/mach-smdk2440.c
arch/arm/mach-s3c2410/s3c2410.c
arch/arm/mach-s3c2410/s3c2440.c
arch/arm/mach-sa1100/badge4.c
arch/arm/mach-sa1100/cerf.c
arch/arm/mach-sa1100/collie.c
arch/arm/mach-sa1100/generic.c
arch/arm/mach-sa1100/jornada720.c
arch/arm/mach-sa1100/neponset.c
arch/arm/mach-sa1100/pleb.c
arch/arm/mach-sa1100/simpad.c
arch/arm/mach-versatile/core.c
arch/arm/plat-omap/usb.c
arch/i386/Kconfig
arch/i386/kernel/apic.c
arch/i386/kernel/i8259.c
arch/i386/kernel/io_apic.c
arch/i386/kernel/smpboot.c
arch/i386/kernel/time.c
arch/m32r/kernel/setup_m32700ut.c
arch/m32r/kernel/setup_mappi.c
arch/m32r/kernel/setup_mappi2.c
arch/m32r/kernel/setup_mappi3.c
arch/m32r/kernel/setup_opsput.c
arch/mips/au1000/common/platform.c
arch/powerpc/platforms/iseries/lpardata.c
arch/powerpc/platforms/iseries/naca.h [moved from include/asm-ppc64/naca.h with 85% similarity]
arch/powerpc/platforms/iseries/release_data.h
arch/powerpc/platforms/iseries/setup.c
arch/powerpc/platforms/powermac/setup.c
arch/powerpc/sysdev/dart.h [moved from include/asm-ppc64/dart.h with 94% similarity]
arch/powerpc/sysdev/u3_iommu.c
arch/ppc/platforms/4xx/ibm440ep.c
arch/ppc/platforms/4xx/ibmstb4.c
arch/ppc/platforms/4xx/redwood5.c
arch/ppc/platforms/4xx/redwood6.c
arch/ppc/platforms/chrp_pegasos_eth.c
arch/ppc/platforms/cpci690.c
arch/ppc/platforms/ev64260.c
arch/ppc/platforms/ev64360.c
arch/ppc/platforms/hdpu.c
arch/ppc/platforms/katana.c
arch/ppc/platforms/radstone_ppc7d.c
arch/ppc/syslib/mpc52xx_devices.c
arch/ppc/syslib/mv64x60.c
arch/ppc/syslib/pq2_devices.c
arch/ppc64/kernel/udbg_scc.c
arch/sh/boards/superh/microdev/setup.c
arch/um/drivers/net_kern.c
arch/um/drivers/ubd_kern.c
arch/xtensa/platform-iss/network.c
drivers/base/platform.c
drivers/block/cfq-iosched.c
drivers/block/floppy.c
drivers/block/noop-iosched.c
drivers/char/s3c2410-rtc.c
drivers/char/sonypi.c
drivers/char/tb0219.c
drivers/char/vr41xx_giu.c
drivers/char/vr41xx_rtc.c
drivers/char/watchdog/mpcore_wdt.c
drivers/char/watchdog/mv64x60_wdt.c
drivers/char/watchdog/s3c2410_wdt.c
drivers/eisa/virtual_root.c
drivers/firmware/dcdbas.c
drivers/firmware/dell_rbu.c
drivers/hwmon/hdaps.c
drivers/i2c/busses/i2c-iop3xx.c
drivers/i2c/busses/i2c-isa.c
drivers/i2c/busses/i2c-ixp2000.c
drivers/i2c/busses/i2c-ixp4xx.c
drivers/i2c/busses/i2c-mpc.c
drivers/i2c/busses/i2c-mv64xxx.c
drivers/i2c/busses/i2c-pxa.c
drivers/i2c/busses/i2c-s3c2410.c
drivers/i2c/chips/isp1301_omap.c
drivers/i2c/i2c-core.c
drivers/i2c/i2c-dev.c
drivers/input/evdev.c
drivers/input/input.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/corgikbd.c
drivers/input/keyboard/lkkbd.c
drivers/input/keyboard/spitzkbd.c
drivers/input/misc/pcspkr.c
drivers/input/mouse/Kconfig
drivers/input/serio/ct82c710.c
drivers/input/serio/i8042.c
drivers/input/serio/maceps2.c
drivers/input/serio/q40kbd.c
drivers/input/serio/rpckbd.c
drivers/input/touchscreen/corgi_ts.c
drivers/macintosh/adbhid.c
drivers/mfd/mcp-sa11x0.c
drivers/misc/hdpuftrs/hdpu_cpustate.c
drivers/misc/hdpuftrs/hdpu_nexus.c
drivers/mmc/pxamci.c
drivers/mmc/wbsd.c
drivers/mtd/maps/bast-flash.c
drivers/mtd/maps/integrator-flash.c
drivers/mtd/maps/ixp2000.c
drivers/mtd/maps/ixp4xx.c
drivers/mtd/maps/omap_nor.c
drivers/mtd/maps/plat-ram.c
drivers/mtd/maps/sa1100-flash.c
drivers/mtd/nand/s3c2410.c
drivers/net/depca.c
drivers/net/dm9000.c
drivers/net/gianfar.c
drivers/net/gianfar_mii.c
drivers/net/irda/pxaficp_ir.c
drivers/net/irda/sa1100_ir.c
drivers/net/irda/smsc-ircc2.c
drivers/net/jazzsonic.c
drivers/net/macsonic.c
drivers/net/mipsnet.c
drivers/net/mv643xx_eth.c
drivers/net/smc91x.c
drivers/net/tokenring/proteon.c
drivers/net/tokenring/skisa.c
drivers/pcmcia/au1000_generic.c
drivers/pcmcia/hd64465_ss.c
drivers/pcmcia/i82365.c
drivers/pcmcia/m32r_cfc.c
drivers/pcmcia/m32r_pcc.c
drivers/pcmcia/omap_cf.c
drivers/pcmcia/pxa2xx_base.c
drivers/pcmcia/pxa2xx_mainstone.c
drivers/pcmcia/pxa2xx_sharpsl.c
drivers/pcmcia/sa1100_generic.c
drivers/pcmcia/tcic.c
drivers/pcmcia/vrc4171_card.c
drivers/scsi/hosts.c
drivers/scsi/ide-scsi.c
drivers/scsi/libata-core.c
drivers/scsi/libata-scsi.c
drivers/scsi/libata.h
drivers/serial/8250.c
drivers/serial/imx.c
drivers/serial/mpc52xx_uart.c
drivers/serial/mpsc.c
drivers/serial/pxa.c
drivers/serial/s3c2410.c
drivers/serial/sa1100.c
drivers/serial/vr41xx_siu.c
drivers/usb/gadget/dummy_hcd.c
drivers/usb/gadget/lh7a40x_udc.c
drivers/usb/gadget/omap_udc.c
drivers/usb/gadget/pxa2xx_udc.c
drivers/usb/host/isp116x-hcd.c
drivers/usb/host/ohci-au1xxx.c
drivers/usb/host/ohci-lh7a404.c
drivers/usb/host/ohci-omap.c
drivers/usb/host/ohci-ppc-soc.c
drivers/usb/host/ohci-pxa27x.c
drivers/usb/host/ohci-s3c2410.c
drivers/usb/host/pci-quirks.c
drivers/usb/host/sl811-hcd.c
drivers/usb/host/sl811_cs.c
drivers/video/acornfb.c
drivers/video/arcfb.c
drivers/video/backlight/corgi_bl.c
drivers/video/dnfb.c
drivers/video/epson1355fb.c
drivers/video/gbefb.c
drivers/video/imxfb.c
drivers/video/pxafb.c
drivers/video/q40fb.c
drivers/video/s1d13xxxfb.c
drivers/video/s3c2410fb.c
drivers/video/sa1100fb.c
drivers/video/sgivwfb.c
drivers/video/vesafb.c
drivers/video/vfb.c
drivers/video/w100fb.c
fs/fs-writeback.c
fs/ntfs/ChangeLog
fs/ntfs/Makefile
fs/ntfs/aops.c
fs/ntfs/attrib.c
fs/ntfs/attrib.h
fs/ntfs/file.c
fs/ntfs/inode.c
fs/ntfs/layout.h
fs/ntfs/lcnalloc.c
fs/ntfs/lcnalloc.h
fs/ntfs/malloc.h
fs/ntfs/mft.c
fs/ntfs/super.c
include/asm-i386/apic.h
include/asm-i386/hw_irq.h
include/asm-i386/mach-default/smpboot_hooks.h
include/asm-i386/mach-visws/smpboot_hooks.h
include/asm-powerpc/numnodes.h [new file with mode: 0644]
include/asm-powerpc/sparsemem.h [moved from include/asm-ppc64/sparsemem.h with 77% similarity]
include/asm-ppc/ppc_sys.h
include/asm-ppc64/numnodes.h [deleted file]
include/linux/device.h
include/linux/platform_device.h [new file with mode: 0644]
include/linux/serial_8250.h
include/sound/emu10k1.h
init/main.c
sound/arm/pxa2xx-ac97.c
sound/core/init.c
sound/pci/emu10k1/emu10k1_main.c

index a5fbc8e897fa6f2f4a768241c66372de7b3366e7..614de31249019d6ab29d0f59a0fccb90c971e5e1 100644 (file)
@@ -50,9 +50,14 @@ userspace utilities, etc.
 Features
 ========
 
-- This is a complete rewrite of the NTFS driver that used to be in the kernel.
-  This new driver implements NTFS read support and is functionally equivalent
-  to the old ntfs driver.
+- This is a complete rewrite of the NTFS driver that used to be in the 2.4 and
+  earlier kernels.  This new driver implements NTFS read support and is
+  functionally equivalent to the old ntfs driver and it also implements limited
+  write support.  The biggest limitation at present is that files/directories
+  cannot be created or deleted.  See below for the list of write features that
+  are so far supported.  Another limitation is that writing to compressed files
+  is not implemented at all.  Also, neither read nor write access to encrypted
+  files is so far implemented.
 - The new driver has full support for sparse files on NTFS 3.x volumes which
   the old driver isn't happy with.
 - The new driver supports execution of binaries due to mmap() now being
@@ -78,7 +83,20 @@ Features
 - The new driver supports fsync(2), fdatasync(2), and msync(2).
 - The new driver supports readv(2) and writev(2).
 - The new driver supports access time updates (including mtime and ctime).
-
+- The new driver supports truncate(2) and open(2) with O_TRUNC.  But at present
+  only very limited support for highly fragmented files, i.e. ones which have
+  their data attribute split across multiple extents, is included.  Another
+  limitation is that at present truncate(2) will never create sparse files,
+  since to mark a file sparse we need to modify the directory entry for the
+  file and we do not implement directory modifications yet.
+- The new driver supports write(2) which can both overwrite existing data and
+  extend the file size so that you can write beyond the existing data.  Also,
+  writing into sparse regions is supported and the holes are filled in with
+  clusters.  But at present only limited support for highly fragmented files,
+  i.e. ones which have their data attribute split across multiple extents, is
+  included.  Another limitation is that write(2) will never create sparse
+  files, since to mark a file sparse we need to modify the directory entry for
+  the file and we do not implement directory modifications yet.
 
 Supported mount options
 =======================
@@ -439,6 +457,22 @@ ChangeLog
 
 Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
 
+2.1.25:
+       - Write support is now extended with write(2) being able to both
+         overwrite existing file data and to extend files.  Also, if a write
+         to a sparse region occurs, write(2) will fill in the hole.  Note,
+         mmap(2) based writes still do not support writing into holes or
+         writing beyond the initialized size.
+       - Write support has a new feature and that is that truncate(2) and
+         open(2) with O_TRUNC are now implemented thus files can be both made
+         smaller and larger.
+       - Note: Both write(2) and truncate(2)/open(2) with O_TRUNC still have
+         limitations in that they
+         - only provide limited support for highly fragmented files.
+         - only work on regular, i.e. uncompressed and unencrypted files.
+         - never create sparse files although this will change once directory
+           operations are implemented.
+       - Lots of bug fixes and enhancements across the board.
 2.1.24:
        - Support journals ($LogFile) which have been modified by chkdsk.  This
          means users can boot into Windows after we marked the volume dirty.
index 5cdb4122f057ca64874024d50b1750f9375cb649..ad55680726ed5b10fac7efb9852d9294d940cf26 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 
index 21e2a518ad3ab71c85edf68e47468e04666d1866..174aa86ee816b49b2e41dcd2e2cfca64b2c3abbf 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/dma-mapping.h>
index 4af0cf5f3bfbc8d4f8fce84fc508f465c6ddf10a..bb4eff61441307458f92a104b0fa655542e118b7 100644 (file)
@@ -13,8 +13,7 @@
 
 #include <linux/device.h>
 #include <linux/string.h>
-#include <linux/slab.h>
-
+#include <linux/platform_device.h>
 #include <asm/io.h>
 #include <asm/hardware/scoop.h>
 
index 0c53dab8090593028381a6c23cdf265779745eb6..4e706d9ad368ee63950a5cea90684eb50b272a26 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/dma-mapping.h>
index db9078ad008c550a0b5c924684df12f280a359e2..d75c8221d2a595d8fddfbf790826be7226797b45 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/string.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/setup.h>
 #include <asm/types.h>
index 837d7f0bda4c8f9b610064551414e66f1cf5a213..37613ad68366eda2615d384501dbcfc4dbb6ffa7 100644 (file)
@@ -22,7 +22,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  */
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
index 4cbdc1fe04b1cff7e0f9315b1367f6a0d24b396d..708e1b3faa1466cddb97867403510c8d301824a3 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <linux/device.h>
 #include <linux/init.h>
+#include <linux/platform_device.h>
 #include <asm/system.h>
 #include <asm/hardware.h>
 #include <asm/irq.h>
index 764ceb49470a88975044d383cfe90e083fd77c70..4c0f7c65facf93f215e510d628b9c17d9cdbcb00 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/list.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/sysdev.h>
index aa34c58b96c411abda5198ffaa7d44ec53f290d2..93f7ccb22c27f4e12ad4dc9ba180baa3ac177aed 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/list.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
 #include <linux/string.h>
index bb5091223b638120291b9ee15e78693ff227638f..80770233b8d40d7970eee8eaf54ed9df2e715da4 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/init.h>
 #include <linux/major.h>
 #include <linux/fs.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/serial.h>
 #include <linux/tty.h>
 #include <linux/serial_core.h>
index a2533c3ab42f6671f26d5d84250b30410e5ac1f7..53f60614498be508777a95ad4fd87f1532b74001 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/init.h>
 #include <linux/major.h>
 #include <linux/fs.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/serial.h>
 #include <linux/tty.h>
 #include <linux/serial_core.h>
index 9aa54de447407d615211860f693fe6a6879f2f28..643f5e1c3d9311d70a9c589f34ca344cc979760c 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/serial.h>
 #include <linux/tty.h>
 #include <linux/serial_core.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
index 8b4a839b6279baf95c13eef696d4969623f7d418..05dfcb48c2b62839eb66dfb8dec0eed8fe72e3d5 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/bitops.h>
 #include <linux/pci.h>
 #include <linux/ioport.h>
index fee1d7b73503f67853e1f86f65879de9c70590b9..b21249908ae4185819ce2f24588a0462371c0332 100644 (file)
@@ -29,7 +29,7 @@
 #include <linux/serial.h>
 #include <linux/tty.h>
 #include <linux/serial_core.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
index 6c396447c4e078d8ce7e1807c072edfc8c704bb7..f3c687cf00713dcbcaf20eb4689aac5f05818b98 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/serial.h>
 #include <linux/sched.h>
 #include <linux/tty.h>
+#include <linux/platform_device.h>
 #include <linux/serial_core.h>
 #include <linux/bootmem.h>
 #include <linux/interrupt.h>
index a20eabc132b08464c43d65d8915051316cf91b0a..4eb962fdb3a8554530887966f80d7b71ace4eeb3 100644 (file)
@@ -10,7 +10,7 @@
 
 #include <linux/tty.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 
 #include <asm/hardware.h>
index d46a70063b0c61821088ae2c8f483c6a9356454f..4ee6bd8a50b8b105699e4c5698883bcd4526647f 100644 (file)
@@ -21,7 +21,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
index 2798613696fa7e0781609bb61132858d25089e27..fc824361430d86951f184e8b23f82cee8ee07928 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/init.h>
 #include <linux/major.h>
 #include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/errno.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
index fd9183ff2ed53c03252acbb5faa86f687c567ed4..a2eac853b2da863fe0f0a59c4bfc90dafa6c092f 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
index d904e643f5ec2c30fabf5d8438f5e40cdec82ad1..c851c2e4dfcb74cc97f988feb93308f53d159500 100644 (file)
@@ -11,7 +11,7 @@
  */
 
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
index 21103df50415a7007428f04224b54c8c2127b023..a88524e7c315ba1f95a423f5408d5250214cf540 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 
 #include <linux/mtd/mtd.h>
index 2ba26e239108f87e9b51f38a6ade736256ffd366..354b157acb3a2071193c450630e846618db69020 100644 (file)
@@ -13,7 +13,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
index bf30b1acda0b97a1c9795a0006622c7db6935265..3f018b296861e4def60cd01df13fb470a4fefd54 100644 (file)
@@ -13,7 +13,7 @@
  */
 
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
index e8b3981444cd0d9da280a58374efa28bd9154bdb..3c5d901efeaa88d94b17be82c029c3ba082dab31 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/io.h>
index 656f73bbcb5a9d6c80fb130b95c1cf6e954764ce..eb5f6d744a4a77778d290ba063b18cccd01973d5 100644 (file)
@@ -14,7 +14,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/major.h>
 #include <linux/fs.h>
 #include <linux/interrupt.h>
index 370df113dc066433640874481e7d055a931a7f39..54162ba954142d615bb47832128e288c90ba023f 100644 (file)
@@ -17,7 +17,7 @@
 
 #include <linux/delay.h>
 #include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/module.h>
 #include <asm/arch/akita.h>
 #include <asm/arch/corgi.h>
index 136c269db0b7965c8b2a93e9c4abc326ab56714d..591e5f32dbeccdb726ab491b31d080f60376b54b 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <asm/hardware.h>
 #include <asm/mach-types.h>
 
index 9c0289333301e9c5f0c0dc252c9990fbd20a8646..9b48a90aefce9d4a25910f513d190c1e7ae7d8c5 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ioport.h>
 #include <linux/pm.h>
 #include <linux/string.h>
index 01a83ab09ac340a8f7fadb0792a948c067c93e08..7de159e2ab42e173b90d3a84d8b2e0b894f812fa 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/fb.h>
 
 #include <asm/setup.h>
index beccf455f796f5a0b1d3be968ba63e2692ada432..1f6857d7747d8d5ac0ddc777c728bb08a864552a 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/sysdev.h>
 #include <linux/major.h>
 #include <linux/fb.h>
index a48c64026e1fbb4be74321b305874b20ad7828b0..887a8cb7b7219097464a44ab477ff0ae98bce320 100644 (file)
@@ -14,7 +14,7 @@
  */
 
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/sysdev.h>
 #include <linux/interrupt.h>
 #include <linux/sched.h>
index 6d413f6701a77ebd8afdeba39ec4a09fd1118081..ad6a13f95a62cfcae51bc94ffc2fd1f243f17be7 100644 (file)
@@ -16,7 +16,7 @@
  */
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/fb.h>
 
 #include <asm/hardware.h>
index 09a5d593f04b27c2558850e76c0e99dcee861967..c722a9a91fcce243b9fce9a77f2c15f3e50b939a 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/pm.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/irq.h>
index b838842b6a2050438cc351698cbf63a54ae68778..6c6878cd2207db94f3b70343beef634fbb6491f2 100644 (file)
@@ -14,7 +14,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/major.h>
 #include <linux/fs.h>
index 8b3d5dc35de58866dd7ddda31235ac1929c09437..82e8253b1fa04d556c4da51b2db7ca905bb642c3 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/err.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/sysdev.h>
 
 #include <linux/interrupt.h>
index ca366e9e264da5c908b072e49fa3fdd43a995cdd..687fe371369d5e42d01fe5fb988b6f536e86b0f5 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/irq.h>
index 08bc7d95a45d78c0290b8d5c15eef4c934fd8105..f58406e6ef5a69a3d65103597813ecffdd3a587d 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index d6328f96728bfb68d72ff068300f1fe3f9638fb8..52c4bab5c7615590ec167bfe0658ba2fe1a2e663 100644 (file)
@@ -15,6 +15,7 @@
  *     10-Feb-2005 BJD  Added camera from guillaume.gourat@nexvision.tv
 */
 #include <linux/config.h>
+#include <linux/platform_device.h>
 
 extern struct platform_device *s3c24xx_uart_devs[];
 
index 5ae80f4e3e672ca3dcf187aa4e96fdc1f27a1752..8390b685c2b61853ac7ead39016427e660a79447 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index c1b5c63ec24a7b31bf7a838158a2e3cb6964a10b..0b71c896bbd1ac5fe5ebb9ae55487cf01992e9d4 100644 (file)
@@ -41,7 +41,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dm9000.h>
 
 #include <asm/mach/arch.h>
index 7efeaaad2361e71f2b75d4b2beb29ae493a329b1..0aa8760598f74bf0d27404005623a938693dc19d 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index 5c0f2b091f95146d850d2ee4197f9b5b6d4dcbf1..378d640ab00bd0029f3e5cb89831319d43eddbfe 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/timer.h>
 #include <linux/init.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/kthread.h>
 
 #include <asm/mach/arch.h>
index c22f8216032d8d8a5e88c5d7e01bfc51bb580253..42b0eeff2e0f1da322805b24d0941f75f357fa33 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/timer.h>
 #include <linux/init.h>
 #include <linux/string.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <linux/mtd/map.h>
 
index ad1459e402e286671dc3ded975a3416b24c6a69e..a2eb9ed48fcdc1e53541a1134c6dd703d3d4361d 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index 22d9e070fd68f42e0e9275a1f97a0fdc258b96c0..8f2a90bf940bc5178ca40e419bef665ea58cda64 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/init.h>
 #include <linux/tty.h>
 #include <linux/console.h>
+#include <linux/platform_device.h>
 #include <linux/serial_core.h>
 #include <linux/serial.h>
 
index 2eda55a6b678bddd2b5fdb6f7c7f17db9bebeb86..2c91965ee1c89292e104703c6111029234449d2d 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index 6950e61b79149022c368beac1a6d2b910c812f9c..d666c621ad064a3811885a792171c1ad82a3e762 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index a8bf5ec826028c44db4b0ef22b81dc11421affdd..0a2013a7654922161631ae5da0043834f9ed7e7e 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
index 833fa36bce057764843e3be433d406fd1ec2f3ed..4d63e7133b48438014250da15e99b79f71637afb 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/list.h>
 #include <linux/timer.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/sysdev.h>
 
 #include <asm/mach/arch.h>
index c92cebff7f8e758b653b053c612b82cb8041ba92..edccd5eb06be917b257d0ec38f1e135734be7243 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/tty.h>
 #include <linux/mtd/mtd.h>
index 23cb748852751db95f0437e97c4052f2dd1865c4..508593722bc741527ffdc276342550ad7c93f860 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/tty.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 
index 7fd6e29c36b79d6fb1cc7091695a6c5a4b56771a..522abc036d3a35521bc9acfe4721c5e2cbd025ed 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/kernel.h>
 #include <linux/tty.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/timer.h>
index 83eba8b54816681f217807dd2ef0dccd76d10288..2abdc419e9848a8056918d62cdb72a1841bdb806 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/cpufreq.h>
 #include <linux/ioport.h>
 #include <linux/sched.h>       /* just for sched_clock() - funny that */
+#include <linux/platform_device.h>
 
 #include <asm/div64.h>
 #include <asm/hardware.h>
index 89af0c831e8fde6621856dec34c23dc0dfef774b..2f671cc3cb99f793a1a186ccae7eeffebd699883 100644 (file)
@@ -6,7 +6,7 @@
 #include <linux/kernel.h>
 #include <linux/tty.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ioport.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
index 052e4caedb89322e510d0eba42bc742a9c3850bd..69f1970646c628e3a9e8f68a2adddf511a16297b 100644 (file)
@@ -8,7 +8,7 @@
 #include <linux/tty.h>
 #include <linux/ioport.h>
 #include <linux/serial_core.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 
 #include <asm/hardware.h>
index e17b58fb9c9c310aa3cf91b3bb783ecd55e52b1c..58c18f9e9b7bf2a207d2ff03be48820609683980 100644 (file)
@@ -6,7 +6,7 @@
 #include <linux/kernel.h>
 #include <linux/tty.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <linux/mtd/partitions.h>
 
index cfb6658e5cdf5fac50e4d9256947d47268483f02..439ddc9b06d6f1dda4159ab73579410df6340bab 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/proc_fs.h>
 #include <linux/string.h> 
 #include <linux/pm.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 
index 7e4bdd07f4afb4c82778a4c3c796d39684587103..a1ca46630dda469140f5ef2b2d33919bc6c9551a 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/init.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
 #include <linux/sysdev.h>
 #include <linux/interrupt.h>
 
index 14a836d7ac250a4ebee43b3cca8edf1b5e70cba8..205e2d0b826d4f4ff53f24c73a71970b88be10a2 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/usb_otg.h>
 
 #include <asm/io.h>
index 5383e5e2d9b7e564f675516f69b477b280989e02..bac0da731ee3ae98065d5771148648ad1df53940 100644 (file)
@@ -1042,8 +1042,3 @@ config X86_TRAMPOLINE
        bool
        depends on X86_SMP || (X86_VOYAGER && SMP)
        default y
-
-config PC
-       bool
-       depends on X86 && !EMBEDDED
-       default y
index 9204be6eedb3c5e4d25c1add893359de85fda1ab..7c724ffa08bb9617e4a1834fb20e5924565e281f 100644 (file)
@@ -803,7 +803,6 @@ no_apic:
 
 void __init init_apic_mappings(void)
 {
-       unsigned int orig_apicid;
        unsigned long apic_phys;
 
        /*
@@ -825,11 +824,8 @@ void __init init_apic_mappings(void)
         * Fetch the APIC ID of the BSP in case we have a
         * default configuration (or the MP table is broken).
         */
-       orig_apicid = boot_cpu_physical_apicid;
-       boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID));
-       if ((orig_apicid != -1U) && (orig_apicid != boot_cpu_physical_apicid))
-               printk(KERN_WARNING "Boot APIC ID in local APIC unexpected (%d vs %d)",
-                       orig_apicid, boot_cpu_physical_apicid);
+       if (boot_cpu_physical_apicid == -1U)
+               boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID));
 
 #ifdef CONFIG_X86_IO_APIC
        {
@@ -1259,81 +1255,40 @@ fastcall void smp_error_interrupt(struct pt_regs *regs)
 }
 
 /*
- * This initializes the IO-APIC and APIC hardware.
+ * This initializes the IO-APIC and APIC hardware if this is
+ * a UP kernel.
  */
-int __init APIC_init(void)
+int __init APIC_init_uniprocessor (void)
 {
-       if (enable_local_apic < 0) {
-               printk(KERN_INFO "APIC disabled\n");
-               return -1;
-       }
+       if (enable_local_apic < 0)
+               clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability);
 
-       /* See if we have a SMP configuration or have forced enabled
-        * the local apic.
-        */
-       if (!smp_found_config && !acpi_lapic && !cpu_has_apic) {
-               enable_local_apic = -1;
+       if (!smp_found_config && !cpu_has_apic)
                return -1;
-       }
 
        /*
-        * Complain if the BIOS pretends there is an apic.
-        * Then get out because we don't have an a local apic.
+        * Complain if the BIOS pretends there is one.
         */
        if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid])) {
                printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n",
                        boot_cpu_physical_apicid);
-               printk(KERN_ERR "... forcing use of dummy APIC emulation. (tell your hw vendor)\n");
-               enable_local_apic = -1;
                return -1;
        }
 
        verify_local_APIC();
 
-       /*
-        * Should not be necessary because the MP table should list the boot
-        * CPU too, but we do it for the sake of robustness anyway.
-        * Makes no sense to do this check in clustered apic mode, so skip it
-        */
-       if (!check_phys_apicid_present(boot_cpu_physical_apicid)) {
-               printk("weird, boot CPU (#%d) not listed by the BIOS.\n",
-                               boot_cpu_physical_apicid);
-               physid_set(boot_cpu_physical_apicid, phys_cpu_present_map);
-       }
-
-       /*
-        * Switch from PIC to APIC mode.
-        */
        connect_bsp_APIC();
-       setup_local_APIC();
 
-#ifdef CONFIG_X86_IO_APIC
-       /*
-        * Now start the IO-APICs
-        */
-       if (smp_found_config && !skip_ioapic_setup && nr_ioapics)
-               setup_IO_APIC();
-#endif
-       return 0;
-}
+       phys_cpu_present_map = physid_mask_of_physid(boot_cpu_physical_apicid);
 
-void __init APIC_late_time_init(void)
-{
-       /* Improve our loops per jiffy estimate */
-       loops_per_jiffy = ((1000 + HZ - 1)/HZ)*cpu_khz;
-       boot_cpu_data.loops_per_jiffy = loops_per_jiffy;
-       cpu_data[0].loops_per_jiffy = loops_per_jiffy;
-
-       /* setup_apic_nmi_watchdog doesn't work properly before cpu_khz is
-        * initialized.  So redo it here to ensure the boot cpu is setup
-        * properly.
-        */
-       if (nmi_watchdog == NMI_LOCAL_APIC)
-               setup_apic_nmi_watchdog();
+       setup_local_APIC();
 
 #ifdef CONFIG_X86_IO_APIC
-       if (smp_found_config && !skip_ioapic_setup && nr_ioapics)
-               IO_APIC_late_time_init();
+       if (smp_found_config)
+               if (!skip_ioapic_setup && nr_ioapics)
+                       setup_IO_APIC();
 #endif
        setup_boot_APIC_clock();
+
+       return 0;
 }
index d86f2490928449ce15acfc8ffff5522e11139453..323ef8ab3244e880c1b32468a12122990ba0ceba 100644 (file)
@@ -435,8 +435,4 @@ void __init init_IRQ(void)
                setup_irq(FPU_IRQ, &fpu_irq);
 
        irq_ctx_init(smp_processor_id());
-
-#ifdef CONFIG_X86_LOCAL_APIC
-       APIC_init();
-#endif
 }
index 5a77c52b20a934d2d9a168ee20a1a965201e1818..cc5d7ac5b2e7dfa5a157bb0dffe57971bc037ed4 100644 (file)
@@ -2387,15 +2387,11 @@ void __init setup_IO_APIC(void)
        sync_Arb_IDs();
        setup_IO_APIC_irqs();
        init_IO_APIC_traps();
+       check_timer();
        if (!acpi_ioapic)
                print_IO_APIC();
 }
 
-void __init IO_APIC_late_time_init(void)
-{
-       check_timer();
-}
-
 /*
  *     Called after all the initialization is done. If we didnt find any
  *     APIC bugs then we can allow the modify fast path
index 5a2bbe0c4ffff7ffcc5344d3d7015bd6651061d8..01b618e73ecd14ba6f9dedea08a718865715d266 100644 (file)
@@ -1078,16 +1078,6 @@ void *xquad_portio;
 EXPORT_SYMBOL(xquad_portio);
 #endif
 
-/*
- * Fall back to non SMP mode after errors.
- *
- */
-static __init void disable_smp(void)
-{
-       cpu_set(0, cpu_sibling_map[0]);
-       cpu_set(0, cpu_core_map[0]);
-}
-
 static void __init smp_boot_cpus(unsigned int max_cpus)
 {
        int apicid, cpu, bit, kicked;
@@ -1100,6 +1090,7 @@ static void __init smp_boot_cpus(unsigned int max_cpus)
        printk("CPU%d: ", 0);
        print_cpu_info(&cpu_data[0]);
 
+       boot_cpu_physical_apicid = GET_APIC_ID(apic_read(APIC_ID));
        boot_cpu_logical_apicid = logical_smp_processor_id();
        x86_cpu_to_apicid[0] = boot_cpu_physical_apicid;
 
@@ -1111,27 +1102,68 @@ static void __init smp_boot_cpus(unsigned int max_cpus)
        cpus_clear(cpu_core_map[0]);
        cpu_set(0, cpu_core_map[0]);
 
-       map_cpu_to_logical_apicid();
-
        /*
         * If we couldn't find an SMP configuration at boot time,
         * get out of here now!
         */
        if (!smp_found_config && !acpi_lapic) {
                printk(KERN_NOTICE "SMP motherboard not detected.\n");
-               disable_smp();
+               smpboot_clear_io_apic_irqs();
+               phys_cpu_present_map = physid_mask_of_physid(0);
+               if (APIC_init_uniprocessor())
+                       printk(KERN_NOTICE "Local APIC not detected."
+                                          " Using dummy APIC emulation.\n");
+               map_cpu_to_logical_apicid();
+               cpu_set(0, cpu_sibling_map[0]);
+               cpu_set(0, cpu_core_map[0]);
+               return;
+       }
+
+       /*
+        * Should not be necessary because the MP table should list the boot
+        * CPU too, but we do it for the sake of robustness anyway.
+        * Makes no sense to do this check in clustered apic mode, so skip it
+        */
+       if (!check_phys_apicid_present(boot_cpu_physical_apicid)) {
+               printk("weird, boot CPU (#%d) not listed by the BIOS.\n",
+                               boot_cpu_physical_apicid);
+               physid_set(hard_smp_processor_id(), phys_cpu_present_map);
+       }
+
+       /*
+        * If we couldn't find a local APIC, then get out of here now!
+        */
+       if (APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid]) && !cpu_has_apic) {
+               printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n",
+                       boot_cpu_physical_apicid);
+               printk(KERN_ERR "... forcing use of dummy APIC emulation. (tell your hw vendor)\n");
+               smpboot_clear_io_apic_irqs();
+               phys_cpu_present_map = physid_mask_of_physid(0);
+               cpu_set(0, cpu_sibling_map[0]);
+               cpu_set(0, cpu_core_map[0]);
                return;
        }
 
+       verify_local_APIC();
+
        /*
         * If SMP should be disabled, then really disable it!
         */
-       if (!max_cpus || (enable_local_apic < 0)) {
-               printk(KERN_INFO "SMP mode deactivated.\n");
-               disable_smp();
+       if (!max_cpus) {
+               smp_found_config = 0;
+               printk(KERN_INFO "SMP mode deactivated, forcing use of dummy APIC emulation.\n");
+               smpboot_clear_io_apic_irqs();
+               phys_cpu_present_map = physid_mask_of_physid(0);
+               cpu_set(0, cpu_sibling_map[0]);
+               cpu_set(0, cpu_core_map[0]);
                return;
        }
 
+       connect_bsp_APIC();
+       setup_local_APIC();
+       map_cpu_to_logical_apicid();
+
+
        setup_portio_remap();
 
        /*
@@ -1212,6 +1244,10 @@ static void __init smp_boot_cpus(unsigned int max_cpus)
        cpu_set(0, cpu_sibling_map[0]);
        cpu_set(0, cpu_core_map[0]);
 
+       smpboot_setup_io_apic();
+
+       setup_boot_APIC_clock();
+
        /*
         * Synchronize the TSC with the AP
         */
index 07471bba2dc6a7d10b28a09ef0c2ae119444f797..41c5b2dc6200b0a600a830f42121e2290d5b29d6 100644 (file)
@@ -440,8 +440,8 @@ static int time_init_device(void)
 
 device_initcall(time_init_device);
 
-extern void (*late_time_init)(void);
 #ifdef CONFIG_HPET_TIMER
+extern void (*late_time_init)(void);
 /* Duplicate of time_init() below, with hpet_enable part added */
 static void __init hpet_time_init(void)
 {
@@ -458,11 +458,6 @@ static void __init hpet_time_init(void)
        printk(KERN_INFO "Using %s for high-res timesource\n",cur_timer->name);
 
        time_init_hook();
-
-#ifdef CONFIG_X86_LOCAL_APIC
-       if (enable_local_apic >= 0)
-               APIC_late_time_init();
-#endif
 }
 #endif
 
@@ -487,9 +482,4 @@ void __init time_init(void)
        printk(KERN_INFO "Using %s for high-res timesource\n",cur_timer->name);
 
        time_init_hook();
-
-#ifdef CONFIG_X86_LOCAL_APIC
-       if (enable_local_apic >= 0)
-               late_time_init = APIC_late_time_init;
-#endif
 }
index 708634b685e44ff925bb2d4eca7af5de8173e329..cb76916b014dd49fd0b333951931d3f8192c367d 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/m32r.h>
index 4e709809efc5f98ec1a04771a265dd33d2b5ea6d..501d798cf0508811a4d7fd8e1754e36c40aa9895 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/m32r.h>
index a1d801598aa44b8a449196025fb3d551e55a3a9a..7f2db5bfd626de1eefac4a297c4ab45116563e42 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/m32r.h>
index a76412e883e8cecfb79396fe9ab26e6e0d6d7175..9c79341a7b455711c165da735893f61cad8cef37 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/m32r.h>
index d7b7ec6d30f88c3942eec68d520b05b0a66675a7..1fbb140854e7b728d3c7ecc0f8344809010fee0b 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/m32r.h>
index 1f7b465c8038bab56d5d7d1346544eeb9cc6b733..48d3f54f88f88e23bfb4a134cb9a2fd4c12416c8 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/config.h>
 #include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/resource.h>
index 608431a226077b955bf56f147c4fe207164da147..bb8c91537f35f7f6df54be8724f3b1044267746d 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/bitops.h>
 #include <asm/processor.h>
 #include <asm/ptrace.h>
-#include <asm/naca.h>
 #include <asm/abs_addr.h>
 #include <asm/iseries/it_lp_naca.h>
 #include <asm/lppaca.h>
@@ -23,6 +22,7 @@
 #include <asm/iseries/it_exp_vpd_panel.h>
 #include <asm/iseries/it_lp_queue.h>
 
+#include "naca.h"
 #include "vpd_areas.h"
 #include "spcomm_area.h"
 #include "ipl_parms.h"
similarity index 85%
rename from include/asm-ppc64/naca.h
rename to arch/powerpc/platforms/iseries/naca.h
index d2afe64475975768bd513d732c7b0c3da8813bd4..ab2372eb8d2ea08f72c2f06ed731db076c10ad2d 100644 (file)
@@ -1,7 +1,7 @@
-#ifndef _NACA_H
-#define _NACA_H
+#ifndef _PLATFORMS_ISERIES_NACA_H
+#define _PLATFORMS_ISERIES_NACA_H
 
-/* 
+/*
  * c 2001 PPC 64 Team, IBM Corp
  *
  * This program is free software; you can redistribute it and/or
@@ -21,4 +21,4 @@ struct naca_struct {
 
 extern struct naca_struct naca;
 
-#endif /* _NACA_H */
+#endif /* _PLATFORMS_ISERIES_NACA_H */
index c68b9c3e5caf4d35ccfc1c57e1cb9ddfce83556f..66189fd2e32dfac18de86785311c91839896de0f 100644 (file)
@@ -24,7 +24,7 @@
  * address of the OS's NACA).
  */
 #include <asm/types.h>
-#include <asm/naca.h>
+#include "naca.h"
 
 /*
  * When we IPL a secondary partition, we will check if if the
index 8e5ef62715bb28e24f630a468baadc9ca2dca16b..fda712b421687f2b910dbc99c47de85644449057 100644 (file)
@@ -41,7 +41,6 @@
 #include <asm/firmware.h>
 
 #include <asm/time.h>
-#include <asm/naca.h>
 #include <asm/paca.h>
 #include <asm/cache.h>
 #include <asm/sections.h>
@@ -54,6 +53,7 @@
 #include <asm/iseries/hv_lp_event.h>
 #include <asm/iseries/lpar_map.h>
 
+#include "naca.h"
 #include "setup.h"
 #include "irq.h"
 #include "vpd_areas.h"
index 6f62af597291178b240d388fafb6f21d05632717..d8bdaaf74c9b240f2b958c868588108257685d8a 100644 (file)
@@ -75,6 +75,7 @@
 #include <asm/smu.h>
 #include <asm/pmc.h>
 #include <asm/mpic.h>
+#include <asm/lmb.h>
 
 #include "pmac.h"
 
similarity index 94%
rename from include/asm-ppc64/dart.h
rename to arch/powerpc/sysdev/dart.h
index cdf8a2dec05fe53066b59ca45b7ffc14970a6a20..ea8f0d9eed8a14b752d6eb6eac7089f0fbf35ac3 100644 (file)
@@ -16,8 +16,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#ifndef _ASM_DART_H
-#define _ASM_DART_H
+#ifndef _POWERPC_SYSDEV_DART_H
+#define _POWERPC_SYSDEV_DART_H
 
 
 /* physical base of DART registers */
@@ -56,4 +56,4 @@
 #define DART_PAGE_FACTOR       (PAGE_SHIFT - DART_PAGE_SHIFT)
 
 
-#endif
+#endif /* _POWERPC_SYSDEV_DART_H */
index fba871a1bda5209c954f0553648f365c25b5e0ae..607722178c1a6695616c528c0511e8f22e23d41f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * arch/ppc64/kernel/u3_iommu.c
+ * arch/powerpc/sysdev/u3_iommu.c
  *
  * Copyright (C) 2004 Olof Johansson <olof@austin.ibm.com>, IBM Corporation
  *
 #include <asm/abs_addr.h>
 #include <asm/cacheflush.h>
 #include <asm/lmb.h>
-#include <asm/dart.h>
 #include <asm/ppc-pci.h>
 
+#include "dart.h"
+
 extern int iommu_force_on;
 
 /* Physical base address and size of the DART table */
index 4712de8ff80fd3b750a7f54b9db95578be418c53..65ac0b9c2d05d8c9ccce0ec77af579d4066205bf 100644 (file)
@@ -14,6 +14,7 @@
  */
 #include <linux/init.h>
 #include <linux/module.h>
+#include <linux/platform_device.h>
 #include <platforms/4xx/ibm440ep.h>
 #include <asm/ocp.h>
 #include <asm/ppc4xx_pic.h>
index d90627b68faa5580516d0419fcac699fc3a5e3c8..7e33bb635443e8c2ad459d5cb0a605691d23cb9a 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <linux/init.h>
+#include <linux/platform_device.h>
 #include <asm/ocp.h>
 #include <asm/ppc4xx_pic.h>
 #include <platforms/4xx/ibmstb4.h>
index bee8b4ac8afd221ba6c2a1b566249cb934505897..611ac861804d31f814e460a2f86d3bc6e23a317d 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/pagemap.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ioport.h>
 #include <asm/io.h>
 #include <asm/machdep.h>
index 8b1012994dfc9ffba5297684334ef331e38a7de5..b13116691289fa19171a45803afbb04b538911c5 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/pagemap.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ioport.h>
 #include <asm/io.h>
 #include <asm/ppc4xx_pic.h>
index a9052305c35debfcaf0c036b0cad037f9e94ea02..108a6e265185b443985e86fe95dced1108e39260 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/types.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/mv643xx.h>
 #include <linux/pci.h>
 
index f64ac2acb603e9b41d0b63a09d88c67df8d6ef40..6ca7bcac947421dfbdf52303bfedfdec0ae0d0f9 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/initrd.h>
 #include <linux/root_dev.h>
 #include <linux/mv643xx.h>
+#include <linux/platform_device.h>
 #include <asm/bootinfo.h>
 #include <asm/machdep.h>
 #include <asm/todc.h>
index aa50637a5cfb141fdb7aab84f941488f8557eda7..32358b3fb23654a09ece544b62a9d88b17450013 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/console.h>
 #include <linux/initrd.h>
 #include <linux/root_dev.h>
+#include <linux/platform_device.h>
 #if !defined(CONFIG_SERIAL_MPSC_CONSOLE)
 #include <linux/serial.h>
 #include <linux/tty.h>
index 53388a1c334f7dbe77a8b93c4e2b1d8e9c5ea9e9..b1324564456e80e28c220c113a4608022d9ace2e 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/bootmem.h>
 #include <linux/mtd/physmap.h>
 #include <linux/mv643xx.h>
+#include <linux/platform_device.h>
 #ifdef CONFIG_BOOTIMG
 #include <linux/bootimg.h>
 #endif
index b6a66d5e9d8357177d1701e33b9563ccbc646540..50039a204c2495f739b9e4637ba553a0f3a5f9dd 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/irq.h>
 #include <linux/ide.h>
 #include <linux/seq_file.h>
+#include <linux/platform_device.h>
 
 #include <linux/initrd.h>
 #include <linux/root_dev.h>
index a301c5ac58ddf985244fa5fd8e8fe55f93a614c3..6e58e30ceed101b1aeace5055a19d4a00583b308 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/seq_file.h>
 #include <linux/mtd/physmap.h>
 #include <linux/mv643xx.h>
+#include <linux/platform_device.h>
 #ifdef CONFIG_BOOTIMG
 #include <linux/bootimg.h>
 #endif
index 6f97911c330d4e5d02ff71f12ab2fd1262528b7f..708b8739ecdd97e1fd2d5eaed73f5e475bd6e8f6 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/serial_core.h>
 #include <linux/mv643xx.h>
 #include <linux/netdevice.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/pgtable.h>
index ad5182efca1dd3fe7e845b2b55016c1208b71940..da3c74bfdc9271cb637c9fb90d09b4f0e93724a4 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <linux/fsl_devices.h>
 #include <linux/resource.h>
+#include <linux/platform_device.h>
 #include <asm/mpc52xx.h>
 #include <asm/ppc_sys.h>
 
index a781c50d2f4caed443aa67d6433d41a8914f32fa..94ea346b7b4b8b7def5f003d5a4dfe8ce74623b4 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/string.h>
 #include <linux/spinlock.h>
 #include <linux/mv643xx.h>
+#include <linux/platform_device.h>
 
 #include <asm/byteorder.h>
 #include <asm/io.h>
index 6f88ba93412b0489e81f01d339b48c8ad4e7d577..e960fe9353256b82e611da8226988d679b2ba433 100644 (file)
@@ -13,7 +13,7 @@
 
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ioport.h>
 #include <asm/cpm2.h>
 #include <asm/irq.h>
index c47fd6c635313d7ac66855b2d48d9cd8bfe1b958..820c5355150761997aab89aa10fec5b44a9d1c69 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/types.h>
 #include <asm/udbg.h>
 #include <asm/processor.h>
-#include <asm/naca.h>
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/pmac_feature.h>
index c18919941ec0f52d3f4c90e666a1c67c20d05685..1c1d65fb12df8cd1b5266ee4e7554d586af441bd 100644 (file)
@@ -13,7 +13,7 @@
 
 #include <linux/config.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ioport.h>
 #include <asm/io.h>
 #include <asm/mach/irq.h>
index 1495007bf6c07df5061526d554382287d95e27e2..721e2601a75da2e8b8fbcbb94dc1797831bc0e7c 100644 (file)
@@ -20,6 +20,7 @@
 #include "linux/ctype.h"
 #include "linux/bootmem.h"
 #include "linux/ethtool.h"
+#include "linux/platform_device.h"
 #include "asm/uaccess.h"
 #include "user_util.h"
 #include "kern_util.h"
index f73134333f64ee55c9eb7f87521e89f577400297..b2c86257b0f83cc0f8f254d286db9bf76e4c5e86 100644 (file)
@@ -35,6 +35,7 @@
 #include "linux/blkpg.h"
 #include "linux/genhd.h"
 #include "linux/spinlock.h"
+#include "linux/platform_device.h"
 #include "asm/segment.h"
 #include "asm/uaccess.h"
 #include "asm/irq.h"
index 498d7dced1f486675a55446f96340f84b6d5dff0..0682ffd38175edd2da90dd10da2e71c8bc6d6906 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/ethtool.h>
 #include <linux/rtnetlink.h>
 #include <linux/timer.h>
+#include <linux/platform_device.h>
 
 #include <xtensa/simcall.h>
 
index 08d9cc99c7de134613a92565c9be883ccb194861..d597c922af11969213d3ea8bbe310bc57089d277 100644 (file)
@@ -10,7 +10,7 @@
  * information.
  */
 
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/dma-mapping.h>
index 5281f8e70510cda1717f2bd385e0c19353476fdd..ecacca9c877eb286bdc77eb2aea5906cc3235e18 100644 (file)
@@ -2059,10 +2059,8 @@ static void cfq_put_cfqd(struct cfq_data *cfqd)
        if (!atomic_dec_and_test(&cfqd->ref))
                return;
 
-       blk_put_queue(q);
-
        cfq_shutdown_timer_wq(cfqd);
-       q->elevator->elevator_data = NULL;
+       blk_put_queue(q);
 
        mempool_destroy(cfqd->crq_pool);
        kfree(cfqd->crq_hash);
index 00895477155e69aaac17792f253681d0e4c0a2d0..5eadbb9d4d71afdff8d74ae5e5e4f5b00c05a996 100644 (file)
@@ -177,7 +177,7 @@ static int print_unex = 1;
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/devfs_fs_kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/buffer_head.h> /* for invalidate_buffers() */
 
 /*
index f56b8edb06e42b217b46c9556c23ca30ef6a6063..e54f006e7e603475999cc2465278035ab547c0cf 100644 (file)
@@ -9,6 +9,7 @@
 
 static void elevator_noop_add_request(request_queue_t *q, struct request *rq)
 {
+       rq->flags |= REQ_NOMERGE;
        elv_dispatch_add_tail(q, rq);
 }
 
index 887b8b2d7882780d29bd86bfd56f9140daebbce8..d724c0de4f28334ad1de8d84ad54291f570f0a15 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/fs.h>
 #include <linux/string.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/rtc.h>
 #include <linux/bcd.h>
index f86c15587238e3bfeb0d9d145325375f9a9ca94b..d05067dcea01967fa9d645b08dffc982041b91ef 100644 (file)
@@ -48,6 +48,7 @@
 #include <linux/dmi.h>
 #include <linux/err.h>
 #include <linux/kfifo.h>
+#include <linux/platform_device.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
index eb7058cbf015545ee032fa9d67128fa505d13599..24355b23b2ca8a3aacf77937dd829e66105fac0b 100644 (file)
@@ -17,7 +17,7 @@
  *  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/device.h>
+#include <linux/platform_device.h>
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/module.h>
index 683278bc5241f1e9a14ab8412ea8368aa4bc8855..94641085faf8b66a93ab1c9add8d2cdf49297c1b 100644 (file)
@@ -19,7 +19,7 @@
  *  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/device.h>
+#include <linux/platform_device.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/init.h>
index a6dbe4da030c7712fa69b90d0f89de618428d55c..5e3292df69d8d794a1a0c4a5ccf5f1becbb38591 100644 (file)
@@ -17,7 +17,7 @@
  *  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/device.h>
+#include <linux/platform_device.h>
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
index 75ca84ed4adf5ea728bfa3c05d23bcdaae4fc72c..47a5f6ab48793ab30566b6965b3ba4e7ad83eda6 100644 (file)
@@ -29,7 +29,7 @@
 #include <linux/reboot.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware/arm_twd.h>
 #include <asm/uaccess.h>
index 6d3ff0836c440b90b61697e2bc2c4559f45b085b..04e0d7e9680db15927bd47edbf3c3e8c11829c7e 100644 (file)
@@ -22,6 +22,8 @@
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/watchdog.h>
+#include <linux/platform_device.h>
+
 #include <asm/mv64x60.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
index b732020acadb1962fa693d5346e97dec33c0acfa..e7e20a6d64b025795b8d578ad3eefae0611f8bac 100644 (file)
@@ -44,7 +44,7 @@
 #include <linux/watchdog.h>
 #include <linux/fs.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 
 #include <asm/uaccess.h>
index 15677f20bd85bd9208a41610eea9a2b7b6e70ee3..0f97a0cb0ff431733f27551ffaa779668143000e 100644 (file)
@@ -9,7 +9,7 @@
 
 #include <linux/config.h>
 #include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/eisa.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
index 955537fe9958acd177a6d8dd1f15b9e0b054bcb7..8ed6ddbb9c5d5f1932bb3ff7ca756f37aa39a552 100644 (file)
@@ -20,7 +20,7 @@
  *  GNU General Public License for more details.
  */
 
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/errno.h>
 #include <linux/init.h>
index 4f4ba9b6d1821bce9a53bbdc4470d2caba99c16e..125929c9048f0be47b4e59543d79a5b62a0e0ff6 100644 (file)
@@ -41,7 +41,7 @@
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/blkdev.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/spinlock.h>
 #include <linux/moduleparam.h>
 #include <linux/firmware.h>
index 0015da5668a181aa8f9956cc59539c49483c3d95..1e5dfc7805e289acf2063f95774bf387e6ab4876 100644 (file)
@@ -27,7 +27,7 @@
  */
 
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/input.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
index 9888fae1f37a8f90ea34fd658573162e08b0007c..13752bcb2afdb04fcfd143663e3d73bac2e88ebd 100644 (file)
@@ -35,7 +35,7 @@
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/i2c.h>
 
 #include <asm/io.h>
index 4fdc02411609208310520bf91b7d55875317ad9f..03672c9ca4092ade4a0590d598b38a9fda259147 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/errno.h>
 #include <linux/i2c.h>
 #include <linux/i2c-isa.h>
+#include <linux/platform_device.h>
 
 static u32 isa_func(struct i2c_adapter *adapter);
 
index 42016ee6ef133908f98549199c41a8f6d93f6096..64552a376f2d000291690abe8d4d4d685a768daf 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
index 69303ab65e0488d91799bb509ce95c10e6ae803a..cc652c3508149b73ed03ed3ef990c0365efd3fcc 100644 (file)
@@ -28,7 +28,7 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
index 8491633005b87f007607e0407a5072e0c2c25c96..65b939a059e9b6fd803cacc7699533b3fd58b559 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/platform_device.h>
+
 #include <asm/io.h>
 #include <linux/fsl_devices.h>
 #include <linux/i2c.h>
index d0d2a6f1386e03dcd5ea64ec9e83ca2b4d2dfc55..6b48027b2ee340b9cdab8624c03f7c029abea6b2 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/mv643xx.h>
+#include <linux/platform_device.h>
+
 #include <asm/io.h>
 
 /* Register defines */
index 44b595d90a4a19cd8554ba9a04ebfbc4af9455c2..67ccbea24ba4b54d546f794b385798d5e193d4a1 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/i2c-pxa.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/irq.h>
index 6ced28e90070a90d6cf3eb7ebd1ad4d9653e30b2..1b582262e677ed89ce68b9277e927d4f61046948 100644 (file)
@@ -33,7 +33,7 @@
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/err.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/irq.h>
@@ -918,8 +918,11 @@ static int __init i2c_adap_s3c_init(void)
        int ret;
 
        ret = driver_register(&s3c2410_i2c_driver);
-       if (ret == 0)
-               ret = driver_register(&s3c2440_i2c_driver); 
+       if (ret == 0) {
+               ret = driver_register(&s3c2440_i2c_driver);
+               if (ret)
+                       driver_unregister(&s3c2410_i2c_driver);
+       }
 
        return ret;
 }
index eaa4742e04fa661bda29b62992a58a17ce52a6e0..9dbb72fffbe2520fdcc47d220cb272adf4a81be4 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
 #include <linux/usb.h>
index 02e335a04f095ad546c7689a1cb5ad3abfb06fda..82ea1b7ec9145536439467778300e29dd1ac0e1d 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/init.h>
 #include <linux/idr.h>
 #include <linux/seq_file.h>
+#include <linux/platform_device.h>
 #include <asm/uaccess.h>
 
 
index ea14c8f1c82baa58b7df2e12921e36c720c1bf85..8af0bd1424d21574f6c284bcdd6ea79ed2f92c75 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/init.h>
 #include <linux/i2c.h>
 #include <linux/i2c-dev.h>
+#include <linux/platform_device.h>
 #include <asm/uaccess.h>
 
 static struct i2c_client i2cdev_client_template;
index a4696cd0978ca68c436ad37ceaa66252b4464b68..9f2352bd8348efa34047125a4f72c181dd5d9f93 100644 (file)
@@ -565,6 +565,7 @@ static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned lon
                                                case EV_LED: bits = dev->ledbit; max = LED_MAX; break;
                                                case EV_SND: bits = dev->sndbit; max = SND_MAX; break;
                                                case EV_FF:  bits = dev->ffbit;  max = FF_MAX;  break;
+                                               case EV_SW:  bits = dev->swbit;  max = SW_MAX;  break;
                                                default: return -EINVAL;
                                        }
                                        bit_to_user(bits, max);
@@ -579,6 +580,9 @@ static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned lon
                                if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
                                        bit_to_user(dev->snd, SND_MAX);
 
+                               if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
+                                       bit_to_user(dev->sw, SW_MAX);
+
                                if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) {
                                        int len;
                                        if (!dev->name) return -ENOENT;
index 3b1685ff9d10b38db94defa977cf1e6c43effc38..1a1654caedd53a2ca59390bae8e97791b41298b6 100644 (file)
@@ -730,7 +730,7 @@ static void input_register_classdevice(struct input_dev *dev)
                 "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
 
        path = kobject_get_path(&dev->cdev.class->subsys.kset.kobj, GFP_KERNEL);
-       printk(KERN_INFO "input: %s/%s as %s\n",
+       printk(KERN_INFO "input: %s as %s/%s\n",
                dev->name ? dev->name : "Unspecified device",
                path ? path : "", dev->cdev.class_id);
        kfree(path);
index 571a68691a4ae3f4ce7d27db795696f2491c575d..4a917748fd9ffac79f863675574ffd2155994858 100644 (file)
@@ -13,11 +13,11 @@ menuconfig INPUT_KEYBOARD
 if INPUT_KEYBOARD
 
 config KEYBOARD_ATKBD
-       tristate "AT keyboard" if !PC
+       tristate "AT keyboard" if !X86_PC
        default y
        select SERIO
        select SERIO_LIBPS2
-       select SERIO_I8042 if PC
+       select SERIO_I8042 if X86_PC
        select SERIO_GSCPS2 if GSC
        help
          Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
index 3210d298b3bc01d6e984b89097962876cc27db2e..d00d14bb637a70b9a49125fa308413efd14eca60 100644 (file)
@@ -12,7 +12,7 @@
  */
 
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/init.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
index 7f06780a437f5d435bfd27ff45e459a0b8d56021..9481132532d017ee42d36cecc789a0007e7bca2b 100644 (file)
@@ -441,7 +441,7 @@ lkkbd_interrupt (struct serio *serio, unsigned char data, unsigned int flags,
                        input_sync (lk->dev);
                        break;
                case LK_METRONOME:
-                       DBG (KERN_INFO "Got %#d and don't "
+                       DBG (KERN_INFO "Got LK_METRONOME and don't "
                                        "know how to handle...\n");
                        break;
                case LK_OUTPUT_ERROR:
index cee9c734a048c9102eaeec0090c99a6e7a8656f0..0fa38a559cdfedc0815a4d91f521f111b08fd86d 100644 (file)
@@ -12,7 +12,7 @@
  */
 
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/init.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
index e34633c37fddc5fb52ec27c68b7d146032e91a69..68ac97f101b0369a6f7a358829a3d6fd25dc4910 100644 (file)
@@ -71,7 +71,7 @@ static int __init pcspkr_init(void)
                return -ENOMEM;
 
        pcspkr_dev->name = "PC Speaker";
-       pcspkr_dev->name = "isa0061/input0";
+       pcspkr_dev->phys = "isa0061/input0";
        pcspkr_dev->id.bustype = BUS_ISA;
        pcspkr_dev->id.vendor = 0x001f;
        pcspkr_dev->id.product = 0x0001;
index 537154dd7a873cc8e252bc2cf61f87f01395936e..574b18a523af2df86eaedb9def31a9d629ae1aad 100644 (file)
@@ -17,7 +17,7 @@ config MOUSE_PS2
        default y
        select SERIO
        select SERIO_LIBPS2
-       select SERIO_I8042 if PC
+       select SERIO_I8042 if X86_PC
        select SERIO_GSCPS2 if GSC
        ---help---
          Say Y here if you have a PS/2 mouse connected to your system. This
index dd0f5bd902413bf89810c5c727775c58c421efce..4da6c86b5d76a46979104b2dcc87edadd0cde7e8 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/serio.h>
 #include <linux/errno.h>
 #include <linux/err.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 
index 4bc40f15999603c5f30e9184575078d5c5a56fcb..01e186422021d3a5c594f5b59a376543b817b2e5 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/serio.h>
 #include <linux/err.h>
 #include <linux/rcupdate.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 
index 9880fc145d9051c1be9a02b9d4fe7f70ec76e53d..d857f7081adb03e408778dc0f7ce058e79df08e3 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/err.h>
index 46093c507988e002fa8ee8abf198cd397721d396..b44d255596c25db08eb1c184c29f4f4468e08f14 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/interrupt.h>
 #include <linux/err.h>
 #include <linux/bitops.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
index 106f5eefd89a256bae649d02b7c46a853da58f8c..52c49258f8a4e4dc447dd5a98d89182e38d2b395 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/init.h>
 #include <linux/serio.h>
 #include <linux/err.h>
+#include <linux/platform_device.h>
 
 #include <asm/irq.h>
 #include <asm/hardware.h>
index 0ba3e6562bffeb8762230891ed580904a5a6080b..15e88eeae8d6daf1cacd4216080cffbc7b09f258 100644 (file)
@@ -11,7 +11,7 @@
 
 
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/init.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
index cdb6d028319577ad6dc0293a66c87ad5538872f2..8f02c155fdc0e26693ce5babe3abe1b5e14221be 100644 (file)
@@ -723,6 +723,7 @@ adbhid_input_register(int id, int default_id, int original_handler_id,
 
        sprintf(hid->phys, "adb%d:%d.%02x/input", id, default_id, original_handler_id);
 
+       hid->input = input_dev;
        hid->id = default_id;
        hid->original_handler_id = original_handler_id;
        hid->current_handler_id = current_handler_id;
index 720e7a3263088b881dce613e958af67f0f243586..7daa0ed7331cd9a46ee537f1939af2bf464e2b0e 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/delay.h>
 #include <linux/spinlock.h>
 #include <linux/slab.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/dma.h>
 #include <asm/hardware.h>
index 46de5c9405557b0ddb1166f397539b94742bc38b..9c4dd682ac74f1fcf92fab62e3b666fd2d54afb9 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/miscdevice.h>
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <asm/uaccess.h>
 #include <linux/hdpu_features.h>
 
index c203b27269ea7278437676d863d17445baebf2d8..165f3405df277f3ffef0f88a3028789b0e76e4b2 100644 (file)
@@ -21,7 +21,7 @@
 #include <linux/hdpu_features.h>
 #include <linux/pci.h>
 
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 static int hdpu_nexus_probe(struct device *ddev);
 static int hdpu_nexus_remove(struct device *ddev);
index d575e3a018bc109365d37153b4ecd305b6966789..f31e247b2cbeabf479c912ae896453db70278935 100644 (file)
@@ -20,7 +20,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/dma-mapping.h>
index 3ace875decc4475270aa8abc066471a3abd634bf..942668e93a7460603251e1fbaa567a4152d36cb4 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
index 63104c73ca3c8bb7caadb6e3b038318fcffd06ec..bfe994e59265d57772da814b0b08eab199e2e793 100644 (file)
@@ -34,7 +34,7 @@
 #include <linux/ioport.h>
 #include <linux/device.h>
 #include <linux/slab.h>
-
+#include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
index e39a98a0171c801b44b0eec73730d2ebb8df5120..d14a0185b8f419f97d14595caee88803c64769d9 100644 (file)
@@ -32,7 +32,7 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/init.h>
 
 #include <linux/mtd/mtd.h>
index 1e5d6e1d05f318c1f0e45c866ed465a825fe6b6c..00b9f67580f1566550950327479fd59af1ca2a33 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/slab.h>
 #include <linux/ioport.h>
 #include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
index da316e543237676e8fdf61a6a98923f06d27adca..733a9297a56263eda2c49bfe7db2545f86ed78d6 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/slab.h>
 #include <linux/ioport.h>
 #include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
index fa84566245a7093588a53df13f30774a4d323591..7f370bb794fefae45e192df190d657e7bcd13110 100644 (file)
@@ -30,7 +30,7 @@
  * 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
index a0577ea00c3c974bb1121d5a11d9cac0765c82b4..104576b5be3480cf238f1f4c3ac0a6c9bbfeca8f 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/ioport.h>
 #include <linux/device.h>
 #include <linux/slab.h>
+#include <linux/platform_device.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
index c81bec7b14d5565c97deed108be3155beec98fdd..c8d0da19d897e461a18efaace5076bfc12111d59 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/err.h>
 
 #include <linux/mtd/mtd.h>
index b58ba236a9eb8a5ffd479da0b56d88a041acc860..2df5e47d1f5ce2ec7c16a9ab6018d7e4be6c1eff 100644 (file)
@@ -48,7 +48,7 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/slab.h>
index c4aa5fe2840e721199d1eac7e1bd32978415d480..4d26e5e7d18b39e9dc411e080f1f380c9970296a 100644 (file)
 #include <linux/unistd.h>
 #include <linux/ctype.h>
 #include <linux/moduleparam.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/bitops.h>
 
 #include <asm/uaccess.h>
index abce1f730d00b32df657af0bbbca5525e903eb51..c0af6fb1fbba210c3139bb18f66ad7960afd8780 100644 (file)
@@ -66,6 +66,7 @@
 #include <linux/mii.h>
 #include <linux/dm9000.h>
 #include <linux/delay.h>
+#include <linux/platform_device.h>
 
 #include <asm/delay.h>
 #include <asm/irq.h>
index ae5a2ed3b2640336a656087596206d2e462ee73e..962580f2c4abb2b5fbdcf21e35db626df2400946 100644 (file)
@@ -81,7 +81,7 @@
 #include <linux/if_vlan.h>
 #include <linux/spinlock.h>
 #include <linux/mm.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
index 1eca1dbca7f1a10c94cb5bd6ae95a5cac8f35998..5a74d3d3dbe1763befa68f4823ba4d7797def74d 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/version.h>
+#include <linux/platform_device.h>
 #include <asm/ocp.h>
 #include <linux/crc32.h>
 #include <linux/mii.h>
index b886b07412a67c95f8e64b5203af7ad92722121e..e1aa9910503bf08bf844f8327f26a5419eb9b939 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/interrupt.h>
 #include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
 #include <linux/pm.h>
 
 #include <net/irda/irda.h>
index 06883309916d493636097dddc1e5b15e93359cf5..76e0b9fb5e96a906c9a5ba915d02407ef3b5414e 100644 (file)
@@ -29,7 +29,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <net/irda/irda.h>
index 140b7cdb1f7e8186f43e896a75a0ecf09af9278a..a1d207f2fa68e0c26f54267539ab169dfd40bff0 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/serial_reg.h>
 #include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/dma.h>
index 8423cb6875f06d75ac38f18881199a5f2b8acb41..a74a5cfaf5bc821271e6c1ae2087b1ed6e38a039 100644 (file)
@@ -33,7 +33,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/bootinfo.h>
index 405e18365edef4c353b681937ebf63eb3971def7..e9c999d7eb390103a26031a5c782065cba883c81 100644 (file)
@@ -47,7 +47,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/bootinfo.h>
index f79f7ee72ab89488734bcaba2fd9caf4731ab374..bbffb585b3b383a4582c22ab2078dafd471f1f14 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/sched.h>
 #include <linux/etherdevice.h>
 #include <linux/netdevice.h>
+#include <linux/platform_device.h>
 #include <asm/io.h>
 #include <asm/mips-boards/simint.h>
 
index 8fbba21d975bc4ad7cbde8dcb9d1998acf3501a3..71f2c6705bc32ba018788e0283c76e71871af507 100644 (file)
@@ -39,6 +39,8 @@
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/ethtool.h>
+#include <linux/platform_device.h>
+
 #include <asm/io.h>
 #include <asm/types.h>
 #include <asm/pgtable.h>
index c573bb351d4c9bb11ce15876b7b58e98373cc933..74d5f1a6fdea262919736ebd9c86f45cd05d0a6e 100644 (file)
@@ -77,7 +77,7 @@ static const char version[] =
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/crc32.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/spinlock.h>
 #include <linux/ethtool.h>
 #include <linux/mii.h>
index eb1423ede75cae8032a0729fdb115e54fa9e7c86..d04c918ebef811ad60af0bdede6d5d1047ccb446 100644 (file)
@@ -29,6 +29,7 @@ static const char version[] = "proteon.c: v1.00 02/01/2003 by Jochen Friedrich\n
 #include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/trdevice.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
index 3c7c66204f7474d0005021afc7fe740698fc2d3a..72cf708396be3f87e9badd604ee3b5b3ab41c6a2 100644 (file)
@@ -36,6 +36,7 @@ static const char version[] = "skisa.c: v1.03 09/12/2002 by Jochen Friedrich\n";
 #include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/trdevice.h>
+#include <linux/platform_device.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
index ba48cef3a9dc87faeffa7ba3cc7d355176a6a577..87302c548c24d437fa0722a01d2d832c7a7bdf38 100644 (file)
@@ -42,7 +42,7 @@
 #include <linux/notifier.h>
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
index b57a0b98b4d6b89c521c9172463b3ffc342f9aec..561706ba4499a8fc99eae63081b88212375d222c 100644 (file)
@@ -37,7 +37,7 @@
 #include <asm/errno.h>
 #include <linux/irq.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/hd64465/hd64465.h>
index 4a41f67d185d9f5b181ced34f110d627433f2ce3..7ce455d01cc919a761ecd5de44bfc504c6246714 100644 (file)
@@ -47,7 +47,7 @@
 #include <linux/delay.h>
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/bitops.h>
 #include <asm/irq.h>
 #include <asm/io.h>
index c6ed70ea4812ec557a41d65469e31180c775cfc8..2c22b4b3619d58f6c1c1a5c107597eb724b2a7e5 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/delay.h>
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/bitops.h>
 #include <asm/irq.h>
 #include <asm/io.h>
index 3397ff28de6aa7897963dd3d6a64f8e68483f821..356a6fb416a14010c86c43df9927088038085a50 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/delay.h>
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <asm/irq.h>
 #include <asm/io.h>
 #include <asm/bitops.h>
index 2558c3cc91eca9eef0a6239c402a8c1afcbb092e..47b5ade95bde5ae8093997c208f79ed242a4aaa7 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/delay.h>
index c2a12d53f6c76e9afd8341174db3bb40b79f47c2..7fa18fb814bc7b862478e38c4d3b3a42f3e79b40 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/ioport.h>
 #include <linux/kernel.h>
 #include <linux/spinlock.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/io.h>
index bbe69b07ce50406a1ab6fb3b23901cdca40b90ec..5209d8c7764fc13215135a9d6bfb3f0828ca380e 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <pcmcia/ss.h>
 
index bd924336a49fe041333745766c3358a1ecc020d4..fe5ea36e7de3878e63b7819cab7872c89d8acd49 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 #include <asm/mach-types.h>
 #include <asm/hardware.h>
index acf60ffc8a124515dbe4cbd42355c647b8414ff0..6d441ec75c6a14bc1fa34e9b31cd866f1c6a8397 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/config.h>
+#include <linux/platform_device.h>
 
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
index f158b67f661007c7b9a1ef64fd55a08f9f70d154..e312638643771c27bb75ce8f81c823b809a51a2b 100644 (file)
@@ -44,7 +44,7 @@
 #include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/workqueue.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/bitops.h>
 
 #include <asm/io.h>
index 3d2dca675e02bc9483b8b8828d2ee948f7e6f52c..38a028c725d47e0aaebe529a6a33ff1e24710357 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/spinlock.h>
 #include <linux/sched.h>
 #include <linux/types.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 
index f24d84538fd56ab5c1d2cdd8df3c509fac8e5092..71dd1ebbe58f26b2740cd77a545e31d6c7472a01 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/init.h>
 #include <linux/completion.h>
 #include <linux/transport_class.h>
+#include <linux/platform_device.h>
 
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
index 00d6a6657ebc3ec78f19d28e25edb2e3354513d4..a440ea38efaa473c11ff379b8564604ca64d669c 100644 (file)
@@ -180,12 +180,22 @@ static void idescsi_input_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigne
                        return;
                }
                count = min(pc->sg->length - pc->b_count, bcount);
-               buf = kmap_atomic(pc->sg->page, KM_IRQ0);
-               drive->hwif->atapi_input_bytes(drive,
-                               buf + pc->b_count + pc->sg->offset, count);
-               kunmap_atomic(buf, KM_IRQ0);
-               bcount -= count;
-               pc->b_count += count;
+               if (PageHighMem(pc->sg->page)) {
+                       unsigned long flags;
+
+                       local_irq_save(flags);
+                       buf = kmap_atomic(pc->sg->page, KM_IRQ0) +
+                                       pc->sg->offset;
+                       drive->hwif->atapi_input_bytes(drive,
+                                               buf + pc->b_count, count);
+                       kunmap_atomic(buf - pc->sg->offset, KM_IRQ0);
+                       local_irq_restore(flags);
+               } else {
+                       buf = page_address(pc->sg->page) + pc->sg->offset;
+                       drive->hwif->atapi_input_bytes(drive,
+                                               buf + pc->b_count, count);
+               }
+               bcount -= count; pc->b_count += count;
                if (pc->b_count == pc->sg->length) {
                        pc->sg++;
                        pc->b_count = 0;
@@ -205,12 +215,22 @@ static void idescsi_output_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsign
                        return;
                }
                count = min(pc->sg->length - pc->b_count, bcount);
-               buf = kmap_atomic(pc->sg->page, KM_IRQ0);
-               drive->hwif->atapi_output_bytes(drive,
-                               buf + pc->b_count + pc->sg->offset, count);
-               kunmap_atomic(buf, KM_IRQ0);
-               bcount -= count;
-               pc->b_count += count;
+               if (PageHighMem(pc->sg->page)) {
+                       unsigned long flags;
+
+                       local_irq_save(flags);
+                       buf = kmap_atomic(pc->sg->page, KM_IRQ0) +
+                                               pc->sg->offset;
+                       drive->hwif->atapi_output_bytes(drive,
+                                               buf + pc->b_count, count);
+                       kunmap_atomic(buf - pc->sg->offset, KM_IRQ0);
+                       local_irq_restore(flags);
+               } else {
+                       buf = page_address(pc->sg->page) + pc->sg->offset;
+                       drive->hwif->atapi_output_bytes(drive,
+                                               buf + pc->b_count, count);
+               }
+               bcount -= count; pc->b_count += count;
                if (pc->b_count == pc->sg->length) {
                        pc->sg++;
                        pc->b_count = 0;
index 8be7dc0b47b849858c8d5b37872e7112fb8e3b31..ff18fa7044c593ebe61ebd184cc45ffbbc91b771 100644 (file)
@@ -294,28 +294,6 @@ void ata_exec_command(struct ata_port *ap, const struct ata_taskfile *tf)
                ata_exec_command_pio(ap, tf);
 }
 
-/**
- *     ata_exec - issue ATA command to host controller
- *     @ap: port to which command is being issued
- *     @tf: ATA taskfile register set
- *
- *     Issues PIO/MMIO write to ATA command register, with proper
- *     synchronization with interrupt handler / other threads.
- *
- *     LOCKING:
- *     Obtains host_set lock.
- */
-
-static inline void ata_exec(struct ata_port *ap, const struct ata_taskfile *tf)
-{
-       unsigned long flags;
-
-       DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command);
-       spin_lock_irqsave(&ap->host_set->lock, flags);
-       ap->ops->exec_command(ap, tf);
-       spin_unlock_irqrestore(&ap->host_set->lock, flags);
-}
-
 /**
  *     ata_tf_to_host - issue ATA taskfile to host controller
  *     @ap: port to which command is being issued
@@ -326,30 +304,11 @@ static inline void ata_exec(struct ata_port *ap, const struct ata_taskfile *tf)
  *     other threads.
  *
  *     LOCKING:
- *     Obtains host_set lock.
- */
-
-static void ata_tf_to_host(struct ata_port *ap, const struct ata_taskfile *tf)
-{
-       ap->ops->tf_load(ap, tf);
-
-       ata_exec(ap, tf);
-}
-
-/**
- *     ata_tf_to_host_nolock - issue ATA taskfile to host controller
- *     @ap: port to which command is being issued
- *     @tf: ATA taskfile register set
- *
- *     Issues ATA taskfile register set to ATA host controller,
- *     with proper synchronization with interrupt handler and
- *     other threads.
- *
- *     LOCKING:
  *     spin_lock_irqsave(host_set lock)
  */
 
-void ata_tf_to_host_nolock(struct ata_port *ap, const struct ata_taskfile *tf)
+static inline void ata_tf_to_host(struct ata_port *ap,
+                                 const struct ata_taskfile *tf)
 {
        ap->ops->tf_load(ap, tf);
        ap->ops->exec_command(ap, tf);
@@ -1912,12 +1871,14 @@ static void ata_bus_post_reset(struct ata_port *ap, unsigned int devmask)
  *
  *     LOCKING:
  *     PCI/etc. bus probe sem.
+ *     Obtains host_set lock.
  *
  */
 
 static unsigned int ata_bus_edd(struct ata_port *ap)
 {
        struct ata_taskfile tf;
+       unsigned long flags;
 
        /* set up execute-device-diag (bus reset) taskfile */
        /* also, take interrupts to a known state (disabled) */
@@ -1928,7 +1889,9 @@ static unsigned int ata_bus_edd(struct ata_port *ap)
        tf.protocol = ATA_PROT_NODATA;
 
        /* do bus reset */
+       spin_lock_irqsave(&ap->host_set->lock, flags);
        ata_tf_to_host(ap, &tf);
+       spin_unlock_irqrestore(&ap->host_set->lock, flags);
 
        /* spec says at least 2ms.  but who knows with those
         * crazy ATAPI devices...
@@ -3555,7 +3518,7 @@ int ata_qc_issue_prot(struct ata_queued_cmd *qc)
 
        switch (qc->tf.protocol) {
        case ATA_PROT_NODATA:
-               ata_tf_to_host_nolock(ap, &qc->tf);
+               ata_tf_to_host(ap, &qc->tf);
                break;
 
        case ATA_PROT_DMA:
@@ -3566,20 +3529,20 @@ int ata_qc_issue_prot(struct ata_queued_cmd *qc)
 
        case ATA_PROT_PIO: /* load tf registers, initiate polling pio */
                ata_qc_set_polling(qc);
-               ata_tf_to_host_nolock(ap, &qc->tf);
+               ata_tf_to_host(ap, &qc->tf);
                ap->hsm_task_state = HSM_ST;
                queue_work(ata_wq, &ap->pio_task);
                break;
 
        case ATA_PROT_ATAPI:
                ata_qc_set_polling(qc);
-               ata_tf_to_host_nolock(ap, &qc->tf);
+               ata_tf_to_host(ap, &qc->tf);
                queue_work(ata_wq, &ap->packet_task);
                break;
 
        case ATA_PROT_ATAPI_NODATA:
                ap->flags |= ATA_FLAG_NOINTR;
-               ata_tf_to_host_nolock(ap, &qc->tf);
+               ata_tf_to_host(ap, &qc->tf);
                queue_work(ata_wq, &ap->packet_task);
                break;
 
@@ -4126,8 +4089,6 @@ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host,
        host->unique_id = ata_unique_id++;
        host->max_cmd_len = 12;
 
-       scsi_assign_lock(host, &host_set->lock);
-
        ap->flags = ATA_FLAG_PORT_DISABLED;
        ap->id = host->unique_id;
        ap->host = host;
index 1e3792f86fcf4359171d46b6ac326b099844e905..248baae9648656f762bd3f15436effee84e4511f 100644 (file)
@@ -39,6 +39,7 @@
 #include <scsi/scsi.h>
 #include "scsi.h"
 #include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
 #include <linux/libata.h>
 #include <linux/hdreg.h>
 #include <asm/uaccess.h>
@@ -2405,8 +2406,12 @@ int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
        struct ata_port *ap;
        struct ata_device *dev;
        struct scsi_device *scsidev = cmd->device;
+       struct Scsi_Host *shost = scsidev->host;
 
-       ap = (struct ata_port *) &scsidev->host->hostdata[0];
+       ap = (struct ata_port *) &shost->hostdata[0];
+
+       spin_unlock(shost->host_lock);
+       spin_lock(&ap->host_set->lock);
 
        ata_scsi_dump_cdb(ap, cmd);
 
@@ -2429,6 +2434,8 @@ int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
                ata_scsi_translate(ap, dev, cmd, done, atapi_xlat);
 
 out_unlock:
+       spin_unlock(&ap->host_set->lock);
+       spin_lock(shost->host_lock);
        return 0;
 }
 
index 10ecd9e15e4fb9296b8a0aa1eeacf99b5aaf2e29..fad051ca4672bf0daf6ed710cf4155f977bb7662 100644 (file)
@@ -48,7 +48,6 @@ extern int ata_qc_issue(struct ata_queued_cmd *qc);
 extern int ata_check_atapi_dma(struct ata_queued_cmd *qc);
 extern void ata_dev_select(struct ata_port *ap, unsigned int device,
                            unsigned int wait, unsigned int can_sleep);
-extern void ata_tf_to_host_nolock(struct ata_port *ap, const struct ata_taskfile *tf);
 extern void swap_buf_le16(u16 *buf, unsigned int buf_words);
 extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg);
 extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
index afb7ddf200e082d8ee6032095f50db6b4dcdd771..f47d2c454e331be473b008398ac66a9f0778ab9f 100644 (file)
@@ -33,7 +33,7 @@
 #include <linux/sysrq.h>
 #include <linux/mca.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial_reg.h>
index 5b3933b0c997f011897585c3cca97fd8b357c2dc..4a54ff5847003855e7643e3f57f64eb0947bb0eb 100644 (file)
@@ -36,7 +36,7 @@
 #include <linux/init.h>
 #include <linux/console.h>
 #include <linux/sysrq.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
@@ -995,6 +995,7 @@ static int __init imx_serial_init(void)
 static void __exit imx_serial_exit(void)
 {
        uart_unregister_driver(&imx_reg);
+       driver_unregister(&serial_imx_driver);
 }
 
 module_init(imx_serial_init);
index 8a79968f8ce1f27b337bbdd21dc0b62c14a241d6..0dd08a09e7e693aded568a69ae827ca436534a7d 100644 (file)
@@ -45,7 +45,7 @@
  */
 
 #include <linux/config.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/tty.h>
 #include <linux/serial.h>
index aec83f577ce6c5c8f7be871af58c9db811a2d0b8..ba8838b234da7b89cfa2575a8689034b5b2cb3e9 100644 (file)
@@ -52,6 +52,8 @@
  * 4) AFAICT, hardware flow control isn't supported by the controller --MAG.
  */
 
+#include <linux/platform_device.h>
+
 #include "mpsc.h"
 
 /*
index 8cc4cedadd995066aae68c73681ea18e4827fd46..16b2f9417af9560aa040034357f06a383495b5b6 100644 (file)
@@ -39,7 +39,7 @@
 #include <linux/circ_buf.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
index 06a17dff1a7316d45e2d2f729b98b99e86a8bb29..036792328d499d4f6286174855897e4bb021ece1 100644 (file)
@@ -63,7 +63,7 @@
 
 #include <linux/module.h>
 #include <linux/ioport.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/init.h>
 #include <linux/sysrq.h>
 #include <linux/console.h>
index c4a789e6af447f3c7d111409cc39972f9df1c479..ed618cc7ae96e132ffcb5e9e32cfbe8de2d22675 100644 (file)
@@ -35,7 +35,7 @@
 #include <linux/init.h>
 #include <linux/console.h>
 #include <linux/sysrq.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
index 2b623ab0e36ec99f64373133e71ba907fe9b9f1b..01696b3e3f619f5b80aa9fcea4dfe4c4540790bb 100644 (file)
@@ -26,7 +26,7 @@
 #endif
 
 #include <linux/console.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/err.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
index 02106bebd5c1ff147dbb070cac83b43d4003758e..975ace3f5b1eac13362d2be4f51c8514ffac5899 100644 (file)
@@ -50,7 +50,7 @@
 #include <linux/list.h>
 #include <linux/interrupt.h>
 #include <linux/version.h>
-
+#include <linux/platform_device.h>
 #include <linux/usb.h>
 #include <linux/usb_gadget.h>
 
index 9b3673904daf33aa4c4575cacac497f8c92cb01f..bc6269f10cbb5d3fd1e1f4a3f0968bea15448c38 100644 (file)
@@ -21,6 +21,8 @@
  *
  */
 
+#include <linux/platform_device.h>
+
 #include "lh7a40x_udc.h"
 
 //#define DEBUG printk
index 41c96b0afbb372b650d26a8503bcaadd2bc9699f..387692a3611e7119560b606611e75d1ac881285f 100644 (file)
@@ -38,7 +38,7 @@
 #include <linux/proc_fs.h>
 #include <linux/mm.h>
 #include <linux/moduleparam.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/usb_ch9.h>
 #include <linux/usb_gadget.h>
 #include <linux/usb_otg.h>
index f83a9262f953364de6b908a733218f67bb7fa564..ee9cd7869d9268723e00451839bb132b6e4f9440 100644 (file)
@@ -43,7 +43,7 @@
 #include <linux/interrupt.h>
 #include <linux/proc_fs.h>
 #include <linux/mm.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/byteorder.h>
index ddb8fc5914663536d45b1c3be6b056f3630e2e88..f9c3f5b8dd1c3adcdcc79dc2f129bf2278b14796 100644 (file)
@@ -70,6 +70,7 @@
 #include <linux/interrupt.h>
 #include <linux/usb.h>
 #include <linux/usb_isp116x.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
index a277e258eb6c5863337a267ea1a46a50cef6caa4..f0c78cf14b6ca6b11ec98010986d54179c41a5b6 100644 (file)
@@ -18,6 +18,8 @@
  * This file is licenced under the GPL.
  */
 
+#include <linux/platform_device.h>
+
 #include <asm/mach-au1x00/au1000.h>
 
 #define USBH_ENABLE_BE (1<<0)
index 238fa4ade615cb1578853f19bda97024479515bc..336c766c6e29f0334b52da96bc72103c4b0e8767 100644 (file)
@@ -16,6 +16,8 @@
  * This file is licenced under the GPL.
  */
 
+#include <linux/platform_device.h>
+
 #include <asm/hardware.h>
 
 
index 49815ec4b842374959f3aab35fa388d290196168..e46cc540cf4d3153fb49d9b942dfdf5652dc4f76 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/signal.h>      /* SA_INTERRUPT */
 #include <linux/jiffies.h>
+#include <linux/platform_device.h>
 
 #include <asm/hardware.h>
 #include <asm/io.h>
index 4832e57ae579e4d17f1361298cb54e218c75128a..92cf6f4a13748ad9535e47d2ce827c6618aa3f75 100644 (file)
@@ -14,6 +14,8 @@
  * This file is licenced under the GPL.
  */
 
+#include <linux/platform_device.h>
+
 /* configure so an HC device and id are always provided */
 /* always called with process context; sleeping is OK */
 
index f4a4aeda40b7e3f1876f20dfa7371adeaba70db7..59e20568e8f947381753f590fdebf8b5467267bb 100644 (file)
@@ -21,6 +21,8 @@
 
 #include <linux/device.h>
 #include <linux/signal.h>
+#include <linux/platform_device.h>
+
 #include <asm/mach-types.h>
 #include <asm/hardware.h>
 #include <asm/arch/pxa-regs.h>
index fab420a2ce712b4bcbcbb6ed38a6ba32b0e18f5c..ee1fc605b402c438729871c87c536ee4b2343200 100644 (file)
@@ -19,6 +19,8 @@
  * This file is licenced under the GPL.
 */
 
+#include <linux/platform_device.h>
+
 #include <asm/hardware.h>
 #include <asm/hardware/clock.h>
 #include <asm/arch/usb-control.h>
index b7fd3f644e1e557bb6924987181eced909f28cd7..b1aa350fd32f04d5b59322bfb0ea946c00c25083 100644 (file)
@@ -138,11 +138,23 @@ reset_needed:
 }
 EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc);
 
+static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask)
+{
+       u16 cmd;
+       return !pci_read_config_word(pdev, PCI_COMMAND, &cmd) && (cmd & mask);
+}
+
+#define pio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_IO)
+#define mmio_enabled(dev) io_type_enabled(dev, PCI_COMMAND_MEMORY)
+
 static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev)
 {
        unsigned long base = 0;
        int i;
 
+       if (!pio_enabled(pdev))
+               return;
+
        for (i = 0; i < PCI_ROM_RESOURCE; i++)
                if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) {
                        base = pci_resource_start(pdev, i);
@@ -153,12 +165,20 @@ static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev)
                uhci_check_and_reset_hc(pdev, base);
 }
 
+static int __devinit mmio_resource_enabled(struct pci_dev *pdev, int idx)
+{
+       return pci_resource_start(pdev, idx) && mmio_enabled(pdev);
+}
+
 static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
 {
        void __iomem *base;
        int wait_time;
        u32 control;
 
+       if (!mmio_resource_enabled(pdev, 0))
+               return;
+
        base = ioremap_nocache(pci_resource_start(pdev, 0),
                                     pci_resource_len(pdev, 0));
        if (base == NULL) return;
@@ -201,6 +221,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
        u32 hcc_params, val, temp;
        u8 cap_length;
 
+       if (!mmio_resource_enabled(pdev, 0))
+               return;
+
        base = ioremap_nocache(pci_resource_start(pdev, 0),
                                pci_resource_len(pdev, 0));
        if (base == NULL) return;
index 40169d9cf2b1855c6b5bdd060e16291423af8aad..5607c0ae683569525358ed5eabeec8c47781eb6d 100644 (file)
@@ -54,6 +54,7 @@
 #include <linux/interrupt.h>
 #include <linux/usb.h>
 #include <linux/usb_sl811.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
index 38aebe361ca140cb8e0255a474a0c5c87144dd8c..e73faf831b24bce4c1a0a16a9b017cdaae274f16 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/ioport.h>
+#include <linux/platform_device.h>
 
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
index f02965f39501666fa9edc28b61a24b47fe2e3561..9b6a39348f81ab4415f01d8837c0990219b88efb 100644 (file)
@@ -26,7 +26,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/fb.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/hardware.h>
index d28457e0c06373c34dfea3dfea8ec916b86bc489..126daff1c848fafba595223580ffbd847322118a 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/arcfb.h>
+#include <linux/platform_device.h>
 
 #include <asm/uaccess.h>
 
index 1991fdb32dfbc7b0d2592f2337c7265755d89d32..4867498f68e8e0b073157e472ce033e725251b60 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/spinlock.h>
 #include <linux/fb.h>
 #include <linux/backlight.h>
index 1dbb82dca40b915649a288bec2f36f9cd8bd9999..1785686a7f11cc8a6120323a6106bc327eb6f345 100644 (file)
@@ -6,6 +6,8 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
 #include <asm/setup.h>
 #include <asm/system.h>
 #include <asm/irq.h>
index 116e808d71cd1819512e197af5aed5efd9b1346c..7363d0b25fdfc12fda931d845e24321e346a82b4 100644 (file)
@@ -54,6 +54,8 @@
 #include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
+#include <linux/platform_device.h>
+
 #include <asm/types.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
index 485604cd446268661ee82aa2bfc6d44bad0d8ae9..316bfe994811f54deadf88da8371b4da2bfbce8d 100644 (file)
@@ -11,7 +11,7 @@
 
 #include <linux/config.h>
 #include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/errno.h>
 #include <linux/fb.h>
index 0b9301facbd356885f2ec2c8727a427358638b37..64d9bcc38da387fee71f375a0cfde75ce2d9bc4b 100644 (file)
@@ -31,7 +31,7 @@
 #include <linux/init.h>
 #include <linux/ioport.h>
 #include <linux/cpufreq.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/hardware.h>
index 6206da9dd5dad23a61c8b901e4bb0e70c7c0c36a..efd9333b05c24001b13a8212be03a91215dbcdaf 100644 (file)
@@ -36,7 +36,7 @@
 #include <linux/init.h>
 #include <linux/ioport.h>
 #include <linux/cpufreq.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/hardware.h>
index 162012bb9264edd2bb58af8b4d9b609e4b940697..8416b2e2b501f7cc723f95f55525f21196a429bd 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/platform_device.h>
 
 #include <asm/uaccess.h>
 #include <asm/setup.h>
index cb2f7a1de947795f3910dbac1794f65d2e496cb8..f4437430dc5f1bf1633ebd1e00f2d152ce31f452 100644 (file)
@@ -30,7 +30,7 @@
 
 #include <linux/config.h>
 #include <linux/module.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/delay.h>
 
 #include <linux/types.h>
index 3862d3cb1fb2d9b1511ef94a80d2b9b72e8c08d8..3cef90456a4b3fb341c10defa60043aad77404b8 100644 (file)
@@ -86,6 +86,7 @@
 #include <linux/interrupt.h>
 #include <linux/workqueue.h>
 #include <linux/wait.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
index 78e5f194b0df3969daba87721acb30b2b370ec28..3d35b28aaac7140affeedd874c4e9e3093b82204 100644 (file)
 #include <linux/init.h>
 #include <linux/ioport.h>
 #include <linux/cpufreq.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <asm/hardware.h>
index 8413907b379a147c1ac112604d7f897ed64a43ce..cf5106eab2d583ee387a9496bae2bc2373739f62 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
+#include <linux/platform_device.h>
+
 #include <asm/io.h>
 #include <asm/mtrr.h>
 
index b1243da55fc5b9c53e96a1d3a6ddbfc802926e2e..3cc23106641db10e1a7787f52c5f9c3ee6d4dcf1 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/fb.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
+#include <linux/platform_device.h>
+
 #include <video/vga.h>
 #include <asm/io.h>
 #include <asm/mtrr.h>
index b137a3fe07525afed563ff99075adc57deb1d1b2..92d46555dd86e2fd02ee7985ef8ba41ca44d1b48 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/vmalloc.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
 #include <asm/uaccess.h>
 #include <linux/fb.h>
 #include <linux/init.h>
index 752bf88906a9de13b6578c8c0f2d989af87e795a..cf8cdb108fd95c0d1eb2f3be7e947804e6a5abb8 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/string.h>
 #include <linux/vmalloc.h>
 #include <asm/io.h>
index ffab4783ac644a984b6c0ff7a529b43720e6bc53..c27f8d4098be3cd713b2934f6eefdc8d03bb4a68 100644 (file)
@@ -247,7 +247,7 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
        wait_queue_head_t *wqh;
 
        if (!atomic_read(&inode->i_count))
-               WARN_ON(!(inode->i_state & I_WILL_FREE));
+               WARN_ON(!(inode->i_state & (I_WILL_FREE|I_FREEING)));
        else
                WARN_ON(inode->i_state & I_WILL_FREE);
 
index de58579a1d0e77a84e4f7d5fcc63c8eadcbb494d..50a7749cfca1bc73c0331ca032db93adaf5f348f 100644 (file)
@@ -1,18 +1,15 @@
 ToDo/Notes:
        - Find and fix bugs.
-       - In between ntfs_prepare/commit_write, need exclusion between
-         simultaneous file extensions.  This is given to us by holding i_sem
-         on the inode.  The only places in the kernel when a file is resized
-         are prepare/commit write and truncate for both of which i_sem is
-         held.  Just have to be careful in readpage/writepage and all other
-         helpers not running under i_sem that we play nice...
-         Also need to be careful with initialized_size extention in
-         ntfs_prepare_write. Basically, just be _very_ careful in this code...
-         UPDATE: The only things that need to be checked are read/writepage
-         which do not hold i_sem.  Note writepage cannot change i_size but it
-         needs to cope with a concurrent i_size change, just like readpage.
-         Also both need to cope with concurrent changes to the other sizes,
-         i.e. initialized/allocated/compressed size, as well.
+       - The only places in the kernel where a file is resized are
+         ntfs_file_write*() and ntfs_truncate() for both of which i_sem is
+         held.  Just have to be careful in read-/writepage and other helpers
+         not running under i_sem that we play nice...  Also need to be careful
+         with initialized_size extension in ntfs_file_write*() and writepage.
+         UPDATE: The only things that need to be checked are the compressed
+         write and the other attribute resize/write cases like index
+         attributes, etc.  For now none of these are implemented so are safe.
+       - Implement filling in of holes in aops.c::ntfs_writepage() and its
+         helpers.
        - Implement mft.c::sync_mft_mirror_umount().  We currently will just
          leave the volume dirty on umount if the final iput(vol->mft_ino)
          causes a write of any mirrored mft records due to the mft mirror
@@ -22,6 +19,68 @@ ToDo/Notes:
        - Enable the code for setting the NT4 compatibility flag when we start
          making NTFS 1.2 specific modifications.
 
+2.1.25 - (Almost) fully implement write(2) and truncate(2).
+
+       - Change ntfs_map_runlist_nolock(), ntfs_attr_find_vcn_nolock() and
+         {__,}ntfs_cluster_free() to also take an optional attribute search
+         context as argument.  This allows calling these functions with the
+         mft record mapped.  Update all callers.
+       - Fix potential deadlock in ntfs_mft_data_extend_allocation_nolock()
+         error handling by passing in the active search context when calling
+         ntfs_cluster_free().
+       - Change ntfs_cluster_alloc() to take an extra boolean parameter
+         specifying whether the cluster are being allocated to extend an
+         attribute or to fill a hole.
+       - Change ntfs_attr_make_non_resident() to call ntfs_cluster_alloc()
+         with @is_extension set to TRUE and remove the runlist terminator
+         fixup code as this is now done by ntfs_cluster_alloc().
+       - Change ntfs_attr_make_non_resident to take the attribute value size
+         as an extra parameter.  This is needed since we need to know the size
+         before we can map the mft record and our callers always know it.  The
+         reason we cannot simply read the size from the vfs inode i_size is
+         that this is not necessarily uptodate.  This happens when
+         ntfs_attr_make_non_resident() is called in the ->truncate call path.
+       - Fix ntfs_attr_make_non_resident() to update the vfs inode i_blocks
+         which is zero for a resident attribute but should no longer be zero
+         once the attribute is non-resident as it then has real clusters
+         allocated.
+       - Add fs/ntfs/attrib.[hc]::ntfs_attr_extend_allocation(), a function to
+         extend the allocation of an attributes.  Optionally, the data size,
+         but not the initialized size can be extended, too.
+       - Implement fs/ntfs/inode.[hc]::ntfs_truncate().  It only supports
+         uncompressed and unencrypted files and it never creates sparse files
+         at least for the moment (making a file sparse requires us to modify
+         its directory entries and we do not support directory operations at
+         the moment).  Also, support for highly fragmented files, i.e. ones
+         whose data attribute is split across multiple extents, is severly
+         limited.  When such a case is encountered, EOPNOTSUPP is returned.
+       - Enable ATTR_SIZE attribute changes in ntfs_setattr().  This completes
+         the initial implementation of file truncation.  Now both open(2)ing
+         a file with the O_TRUNC flag and the {,f}truncate(2) system calls
+         will resize a file appropriately.  The limitations are that only
+         uncompressed and unencrypted files are supported.  Also, there is
+         only very limited support for highly fragmented files (the ones whose
+         $DATA attribute is split into multiple attribute extents).
+       - In attrib.c::ntfs_attr_set() call balance_dirty_pages_ratelimited()
+         and cond_resched() in the main loop as we could be dirtying a lot of
+         pages and this ensures we play nice with the VM and the system as a
+         whole.
+       - Implement file operations ->write, ->aio_write, ->writev for regular
+         files.  This replaces the old use of generic_file_write(), et al and
+         the address space operations ->prepare_write and ->commit_write.
+         This means that both sparse and non-sparse (unencrypted and
+         uncompressed) files can now be extended using the normal write(2)
+         code path.  There are two limitations at present and these are that
+         we never create sparse files and that we only have limited support
+         for highly fragmented files, i.e. ones whose data attribute is split
+         across multiple extents.   When such a case is encountered,
+         EOPNOTSUPP is returned.
+       - $EA attributes can be both resident and non-resident.
+       - Use %z for size_t to fix compilation warnings.  (Andrew Morton)
+       - Fix compilation warnings with gcc-4.0.2 on SUSE 10.0.
+       - Document extended attribute ($EA) NEED_EA flag.  (Based on libntfs
+         patch by Yura Pakhuchiy.)
+
 2.1.24 - Lots of bug fixes and support more clean journal states.
 
        - Support journals ($LogFile) which have been modified by chkdsk.  This
index 894b2b876d353b5c61a81e30d39ef608993f9000..d0d45d1c853a95f495755d9339b92f23b489ee13 100644 (file)
@@ -6,7 +6,7 @@ ntfs-objs := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \
             index.o inode.o mft.o mst.o namei.o runlist.o super.o sysctl.o \
             unistr.o upcase.o
 
-EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.24\"
+EXTRA_CFLAGS = -DNTFS_VERSION=\"2.1.25\"
 
 ifeq ($(CONFIG_NTFS_DEBUG),y)
 EXTRA_CFLAGS += -DDEBUG
index 5e80c07c6a4d2b0a9c141602efeac6f9b5b59614..1c0a4315876aec7ecd7f2e8a42c603562e916f01 100644 (file)
@@ -1391,8 +1391,7 @@ retry_writepage:
                if (NInoEncrypted(ni)) {
                        unlock_page(page);
                        BUG_ON(ni->type != AT_DATA);
-                       ntfs_debug("Denying write access to encrypted "
-                                       "file.");
+                       ntfs_debug("Denying write access to encrypted file.");
                        return -EACCES;
                }
                /* Compressed data streams are handled in compress.c. */
@@ -1508,8 +1507,8 @@ retry_writepage:
        /* Zero out of bounds area in the page cache page. */
        memset(kaddr + attr_len, 0, PAGE_CACHE_SIZE - attr_len);
        kunmap_atomic(kaddr, KM_USER0);
-       flush_dcache_mft_record_page(ctx->ntfs_ino);
        flush_dcache_page(page);
+       flush_dcache_mft_record_page(ctx->ntfs_ino);
        /* We are done with the page. */
        end_page_writeback(page);
        /* Finally, mark the mft record dirty, so it gets written back. */
@@ -1542,830 +1541,6 @@ err_out:
        return err;
 }
 
-/**
- * ntfs_prepare_nonresident_write -
- *
- */
-static int ntfs_prepare_nonresident_write(struct page *page,
-               unsigned from, unsigned to)
-{
-       VCN vcn;
-       LCN lcn;
-       s64 initialized_size;
-       loff_t i_size;
-       sector_t block, ablock, iblock;
-       struct inode *vi;
-       ntfs_inode *ni;
-       ntfs_volume *vol;
-       runlist_element *rl;
-       struct buffer_head *bh, *head, *wait[2], **wait_bh = wait;
-       unsigned long flags;
-       unsigned int vcn_ofs, block_start, block_end, blocksize;
-       int err;
-       BOOL is_retry;
-       unsigned char blocksize_bits;
-
-       vi = page->mapping->host;
-       ni = NTFS_I(vi);
-       vol = ni->vol;
-
-       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
-                       "0x%lx, from = %u, to = %u.", ni->mft_no, ni->type,
-                       page->index, from, to);
-
-       BUG_ON(!NInoNonResident(ni));
-
-       blocksize_bits = vi->i_blkbits;
-       blocksize = 1 << blocksize_bits;
-
-       /*
-        * create_empty_buffers() will create uptodate/dirty buffers if the
-        * page is uptodate/dirty.
-        */
-       if (!page_has_buffers(page))
-               create_empty_buffers(page, blocksize, 0);
-       bh = head = page_buffers(page);
-       if (unlikely(!bh))
-               return -ENOMEM;
-
-       /* The first block in the page. */
-       block = (s64)page->index << (PAGE_CACHE_SHIFT - blocksize_bits);
-
-       read_lock_irqsave(&ni->size_lock, flags);
-       /*
-        * The first out of bounds block for the allocated size.  No need to
-        * round up as allocated_size is in multiples of cluster size and the
-        * minimum cluster size is 512 bytes, which is equal to the smallest
-        * blocksize.
-        */
-       ablock = ni->allocated_size >> blocksize_bits;
-       i_size = i_size_read(vi);
-       initialized_size = ni->initialized_size;
-       read_unlock_irqrestore(&ni->size_lock, flags);
-
-       /* The last (fully or partially) initialized block. */
-       iblock = initialized_size >> blocksize_bits;
-
-       /* Loop through all the buffers in the page. */
-       block_start = 0;
-       rl = NULL;
-       err = 0;
-       do {
-               block_end = block_start + blocksize;
-               /*
-                * If buffer @bh is outside the write, just mark it uptodate
-                * if the page is uptodate and continue with the next buffer.
-                */
-               if (block_end <= from || block_start >= to) {
-                       if (PageUptodate(page)) {
-                               if (!buffer_uptodate(bh))
-                                       set_buffer_uptodate(bh);
-                       }
-                       continue;
-               }
-               /*
-                * @bh is at least partially being written to.
-                * Make sure it is not marked as new.
-                */
-               //if (buffer_new(bh))
-               //      clear_buffer_new(bh);
-
-               if (block >= ablock) {
-                       // TODO: block is above allocated_size, need to
-                       // allocate it. Best done in one go to accommodate not
-                       // only block but all above blocks up to and including:
-                       // ((page->index << PAGE_CACHE_SHIFT) + to + blocksize
-                       // - 1) >> blobksize_bits. Obviously will need to round
-                       // up to next cluster boundary, too. This should be
-                       // done with a helper function, so it can be reused.
-                       ntfs_error(vol->sb, "Writing beyond allocated size "
-                                       "is not supported yet. Sorry.");
-                       err = -EOPNOTSUPP;
-                       goto err_out;
-                       // Need to update ablock.
-                       // Need to set_buffer_new() on all block bhs that are
-                       // newly allocated.
-               }
-               /*
-                * Now we have enough allocated size to fulfill the whole
-                * request, i.e. block < ablock is true.
-                */
-               if (unlikely((block >= iblock) &&
-                               (initialized_size < i_size))) {
-                       /*
-                        * If this page is fully outside initialized size, zero
-                        * out all pages between the current initialized size
-                        * and the current page. Just use ntfs_readpage() to do
-                        * the zeroing transparently.
-                        */
-                       if (block > iblock) {
-                               // TODO:
-                               // For each page do:
-                               // - read_cache_page()
-                               // Again for each page do:
-                               // - wait_on_page_locked()
-                               // - Check (PageUptodate(page) &&
-                               //                      !PageError(page))
-                               // Update initialized size in the attribute and
-                               // in the inode.
-                               // Again, for each page do:
-                               //      __set_page_dirty_buffers();
-                               // page_cache_release()
-                               // We don't need to wait on the writes.
-                               // Update iblock.
-                       }
-                       /*
-                        * The current page straddles initialized size. Zero
-                        * all non-uptodate buffers and set them uptodate (and
-                        * dirty?). Note, there aren't any non-uptodate buffers
-                        * if the page is uptodate.
-                        * FIXME: For an uptodate page, the buffers may need to
-                        * be written out because they were not initialized on
-                        * disk before.
-                        */
-                       if (!PageUptodate(page)) {
-                               // TODO:
-                               // Zero any non-uptodate buffers up to i_size.
-                               // Set them uptodate and dirty.
-                       }
-                       // TODO:
-                       // Update initialized size in the attribute and in the
-                       // inode (up to i_size).
-                       // Update iblock.
-                       // FIXME: This is inefficient. Try to batch the two
-                       // size changes to happen in one go.
-                       ntfs_error(vol->sb, "Writing beyond initialized size "
-                                       "is not supported yet. Sorry.");
-                       err = -EOPNOTSUPP;
-                       goto err_out;
-                       // Do NOT set_buffer_new() BUT DO clear buffer range
-                       // outside write request range.
-                       // set_buffer_uptodate() on complete buffers as well as
-                       // set_buffer_dirty().
-               }
-
-               /* Need to map unmapped buffers. */
-               if (!buffer_mapped(bh)) {
-                       /* Unmapped buffer. Need to map it. */
-                       bh->b_bdev = vol->sb->s_bdev;
-
-                       /* Convert block into corresponding vcn and offset. */
-                       vcn = (VCN)block << blocksize_bits >>
-                                       vol->cluster_size_bits;
-                       vcn_ofs = ((VCN)block << blocksize_bits) &
-                                       vol->cluster_size_mask;
-
-                       is_retry = FALSE;
-                       if (!rl) {
-lock_retry_remap:
-                               down_read(&ni->runlist.lock);
-                               rl = ni->runlist.rl;
-                       }
-                       if (likely(rl != NULL)) {
-                               /* Seek to element containing target vcn. */
-                               while (rl->length && rl[1].vcn <= vcn)
-                                       rl++;
-                               lcn = ntfs_rl_vcn_to_lcn(rl, vcn);
-                       } else
-                               lcn = LCN_RL_NOT_MAPPED;
-                       if (unlikely(lcn < 0)) {
-                               /*
-                                * We extended the attribute allocation above.
-                                * If we hit an ENOENT here it means that the
-                                * allocation was insufficient which is a bug.
-                                */
-                               BUG_ON(lcn == LCN_ENOENT);
-
-                               /* It is a hole, need to instantiate it. */
-                               if (lcn == LCN_HOLE) {
-                                       // TODO: Instantiate the hole.
-                                       // clear_buffer_new(bh);
-                                       // unmap_underlying_metadata(bh->b_bdev,
-                                       //              bh->b_blocknr);
-                                       // For non-uptodate buffers, need to
-                                       // zero out the region outside the
-                                       // request in this bh or all bhs,
-                                       // depending on what we implemented
-                                       // above.
-                                       // Need to flush_dcache_page().
-                                       // Or could use set_buffer_new()
-                                       // instead?
-                                       ntfs_error(vol->sb, "Writing into "
-                                                       "sparse regions is "
-                                                       "not supported yet. "
-                                                       "Sorry.");
-                                       err = -EOPNOTSUPP;
-                                       if (!rl)
-                                               up_read(&ni->runlist.lock);
-                                       goto err_out;
-                               } else if (!is_retry &&
-                                               lcn == LCN_RL_NOT_MAPPED) {
-                                       is_retry = TRUE;
-                                       /*
-                                        * Attempt to map runlist, dropping
-                                        * lock for the duration.
-                                        */
-                                       up_read(&ni->runlist.lock);
-                                       err = ntfs_map_runlist(ni, vcn);
-                                       if (likely(!err))
-                                               goto lock_retry_remap;
-                                       rl = NULL;
-                               } else if (!rl)
-                                       up_read(&ni->runlist.lock);
-                               /*
-                                * Failed to map the buffer, even after
-                                * retrying.
-                                */
-                               if (!err)
-                                       err = -EIO;
-                               bh->b_blocknr = -1;
-                               ntfs_error(vol->sb, "Failed to write to inode "
-                                               "0x%lx, attribute type 0x%x, "
-                                               "vcn 0x%llx, offset 0x%x "
-                                               "because its location on disk "
-                                               "could not be determined%s "
-                                               "(error code %i).",
-                                               ni->mft_no, ni->type,
-                                               (unsigned long long)vcn,
-                                               vcn_ofs, is_retry ? " even "
-                                               "after retrying" : "", err);
-                               goto err_out;
-                       }
-                       /* We now have a successful remap, i.e. lcn >= 0. */
-
-                       /* Setup buffer head to correct block. */
-                       bh->b_blocknr = ((lcn << vol->cluster_size_bits)
-                                       + vcn_ofs) >> blocksize_bits;
-                       set_buffer_mapped(bh);
-
-                       // FIXME: Something analogous to this is needed for
-                       // each newly allocated block, i.e. BH_New.
-                       // FIXME: Might need to take this out of the
-                       // if (!buffer_mapped(bh)) {}, depending on how we
-                       // implement things during the allocated_size and
-                       // initialized_size extension code above.
-                       if (buffer_new(bh)) {
-                               clear_buffer_new(bh);
-                               unmap_underlying_metadata(bh->b_bdev,
-                                               bh->b_blocknr);
-                               if (PageUptodate(page)) {
-                                       set_buffer_uptodate(bh);
-                                       continue;
-                               }
-                               /*
-                                * Page is _not_ uptodate, zero surrounding
-                                * region. NOTE: This is how we decide if to
-                                * zero or not!
-                                */
-                               if (block_end > to || block_start < from) {
-                                       void *kaddr;
-
-                                       kaddr = kmap_atomic(page, KM_USER0);
-                                       if (block_end > to)
-                                               memset(kaddr + to, 0,
-                                                               block_end - to);
-                                       if (block_start < from)
-                                               memset(kaddr + block_start, 0,
-                                                               from -
-                                                               block_start);
-                                       flush_dcache_page(page);
-                                       kunmap_atomic(kaddr, KM_USER0);
-                               }
-                               continue;
-                       }
-               }
-               /* @bh is mapped, set it uptodate if the page is uptodate. */
-               if (PageUptodate(page)) {
-                       if (!buffer_uptodate(bh))
-                               set_buffer_uptodate(bh);
-                       continue;
-               }
-               /*
-                * The page is not uptodate. The buffer is mapped. If it is not
-                * uptodate, and it is only partially being written to, we need
-                * to read the buffer in before the write, i.e. right now.
-                */
-               if (!buffer_uptodate(bh) &&
-                               (block_start < from || block_end > to)) {
-                       ll_rw_block(READ, 1, &bh);
-                       *wait_bh++ = bh;
-               }
-       } while (block++, block_start = block_end,
-                       (bh = bh->b_this_page) != head);
-
-       /* Release the lock if we took it. */
-       if (rl) {
-               up_read(&ni->runlist.lock);
-               rl = NULL;
-       }
-
-       /* If we issued read requests, let them complete. */
-       while (wait_bh > wait) {
-               wait_on_buffer(*--wait_bh);
-               if (!buffer_uptodate(*wait_bh))
-                       return -EIO;
-       }
-
-       ntfs_debug("Done.");
-       return 0;
-err_out:
-       /*
-        * Zero out any newly allocated blocks to avoid exposing stale data.
-        * If BH_New is set, we know that the block was newly allocated in the
-        * above loop.
-        * FIXME: What about initialized_size increments? Have we done all the
-        * required zeroing above? If not this error handling is broken, and
-        * in particular the if (block_end <= from) check is completely bogus.
-        */
-       bh = head;
-       block_start = 0;
-       is_retry = FALSE;
-       do {
-               block_end = block_start + blocksize;
-               if (block_end <= from)
-                       continue;
-               if (block_start >= to)
-                       break;
-               if (buffer_new(bh)) {
-                       void *kaddr;
-
-                       clear_buffer_new(bh);
-                       kaddr = kmap_atomic(page, KM_USER0);
-                       memset(kaddr + block_start, 0, bh->b_size);
-                       kunmap_atomic(kaddr, KM_USER0);
-                       set_buffer_uptodate(bh);
-                       mark_buffer_dirty(bh);
-                       is_retry = TRUE;
-               }
-       } while (block_start = block_end, (bh = bh->b_this_page) != head);
-       if (is_retry)
-               flush_dcache_page(page);
-       if (rl)
-               up_read(&ni->runlist.lock);
-       return err;
-}
-
-/**
- * ntfs_prepare_write - prepare a page for receiving data
- *
- * This is called from generic_file_write() with i_sem held on the inode
- * (@page->mapping->host).  The @page is locked but not kmap()ped.  The source
- * data has not yet been copied into the @page.
- *
- * Need to extend the attribute/fill in holes if necessary, create blocks and
- * make partially overwritten blocks uptodate,
- *
- * i_size is not to be modified yet.
- *
- * Return 0 on success or -errno on error.
- *
- * Should be using block_prepare_write() [support for sparse files] or
- * cont_prepare_write() [no support for sparse files].  Cannot do that due to
- * ntfs specifics but can look at them for implementation guidance.
- *
- * Note: In the range, @from is inclusive and @to is exclusive, i.e. @from is
- * the first byte in the page that will be written to and @to is the first byte
- * after the last byte that will be written to.
- */
-static int ntfs_prepare_write(struct file *file, struct page *page,
-               unsigned from, unsigned to)
-{
-       s64 new_size;
-       loff_t i_size;
-       struct inode *vi = page->mapping->host;
-       ntfs_inode *base_ni = NULL, *ni = NTFS_I(vi);
-       ntfs_volume *vol = ni->vol;
-       ntfs_attr_search_ctx *ctx = NULL;
-       MFT_RECORD *m = NULL;
-       ATTR_RECORD *a;
-       u8 *kaddr;
-       u32 attr_len;
-       int err;
-
-       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
-                       "0x%lx, from = %u, to = %u.", vi->i_ino, ni->type,
-                       page->index, from, to);
-       BUG_ON(!PageLocked(page));
-       BUG_ON(from > PAGE_CACHE_SIZE);
-       BUG_ON(to > PAGE_CACHE_SIZE);
-       BUG_ON(from > to);
-       BUG_ON(NInoMstProtected(ni));
-       /*
-        * If a previous ntfs_truncate() failed, repeat it and abort if it
-        * fails again.
-        */
-       if (unlikely(NInoTruncateFailed(ni))) {
-               down_write(&vi->i_alloc_sem);
-               err = ntfs_truncate(vi);
-               up_write(&vi->i_alloc_sem);
-               if (err || NInoTruncateFailed(ni)) {
-                       if (!err)
-                               err = -EIO;
-                       goto err_out;
-               }
-       }
-       /* If the attribute is not resident, deal with it elsewhere. */
-       if (NInoNonResident(ni)) {
-               /*
-                * Only unnamed $DATA attributes can be compressed, encrypted,
-                * and/or sparse.
-                */
-               if (ni->type == AT_DATA && !ni->name_len) {
-                       /* If file is encrypted, deny access, just like NT4. */
-                       if (NInoEncrypted(ni)) {
-                               ntfs_debug("Denying write access to encrypted "
-                                               "file.");
-                               return -EACCES;
-                       }
-                       /* Compressed data streams are handled in compress.c. */
-                       if (NInoCompressed(ni)) {
-                               // TODO: Implement and replace this check with
-                               // return ntfs_write_compressed_block(page);
-                               ntfs_error(vi->i_sb, "Writing to compressed "
-                                               "files is not supported yet. "
-                                               "Sorry.");
-                               return -EOPNOTSUPP;
-                       }
-                       // TODO: Implement and remove this check.
-                       if (NInoSparse(ni)) {
-                               ntfs_error(vi->i_sb, "Writing to sparse files "
-                                               "is not supported yet. Sorry.");
-                               return -EOPNOTSUPP;
-                       }
-               }
-               /* Normal data stream. */
-               return ntfs_prepare_nonresident_write(page, from, to);
-       }
-       /*
-        * Attribute is resident, implying it is not compressed, encrypted, or
-        * sparse.
-        */
-       BUG_ON(page_has_buffers(page));
-       new_size = ((s64)page->index << PAGE_CACHE_SHIFT) + to;
-       /* If we do not need to resize the attribute allocation we are done. */
-       if (new_size <= i_size_read(vi))
-               goto done;
-       /* Map, pin, and lock the (base) mft record. */
-       if (!NInoAttr(ni))
-               base_ni = ni;
-       else
-               base_ni = ni->ext.base_ntfs_ino;
-       m = map_mft_record(base_ni);
-       if (IS_ERR(m)) {
-               err = PTR_ERR(m);
-               m = NULL;
-               ctx = NULL;
-               goto err_out;
-       }
-       ctx = ntfs_attr_get_search_ctx(base_ni, m);
-       if (unlikely(!ctx)) {
-               err = -ENOMEM;
-               goto err_out;
-       }
-       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
-                       CASE_SENSITIVE, 0, NULL, 0, ctx);
-       if (unlikely(err)) {
-               if (err == -ENOENT)
-                       err = -EIO;
-               goto err_out;
-       }
-       m = ctx->mrec;
-       a = ctx->attr;
-       /* The total length of the attribute value. */
-       attr_len = le32_to_cpu(a->data.resident.value_length);
-       /* Fix an eventual previous failure of ntfs_commit_write(). */
-       i_size = i_size_read(vi);
-       if (unlikely(attr_len > i_size)) {
-               attr_len = i_size;
-               a->data.resident.value_length = cpu_to_le32(attr_len);
-       }
-       /* If we do not need to resize the attribute allocation we are done. */
-       if (new_size <= attr_len)
-               goto done_unm;
-       /* Check if new size is allowed in $AttrDef. */
-       err = ntfs_attr_size_bounds_check(vol, ni->type, new_size);
-       if (unlikely(err)) {
-               if (err == -ERANGE) {
-                       ntfs_error(vol->sb, "Write would cause the inode "
-                                       "0x%lx to exceed the maximum size for "
-                                       "its attribute type (0x%x).  Aborting "
-                                       "write.", vi->i_ino,
-                                       le32_to_cpu(ni->type));
-               } else {
-                       ntfs_error(vol->sb, "Inode 0x%lx has unknown "
-                                       "attribute type 0x%x.  Aborting "
-                                       "write.", vi->i_ino,
-                                       le32_to_cpu(ni->type));
-                       err = -EIO;
-               }
-               goto err_out2;
-       }
-       /*
-        * Extend the attribute record to be able to store the new attribute
-        * size.
-        */
-       if (new_size >= vol->mft_record_size || ntfs_attr_record_resize(m, a,
-                       le16_to_cpu(a->data.resident.value_offset) +
-                       new_size)) {
-               /* Not enough space in the mft record. */
-               ntfs_error(vol->sb, "Not enough space in the mft record for "
-                               "the resized attribute value.  This is not "
-                               "supported yet.  Aborting write.");
-               err = -EOPNOTSUPP;
-               goto err_out2;
-       }
-       /*
-        * We have enough space in the mft record to fit the write.  This
-        * implies the attribute is smaller than the mft record and hence the
-        * attribute must be in a single page and hence page->index must be 0.
-        */
-       BUG_ON(page->index);
-       /*
-        * If the beginning of the write is past the old size, enlarge the
-        * attribute value up to the beginning of the write and fill it with
-        * zeroes.
-        */
-       if (from > attr_len) {
-               memset((u8*)a + le16_to_cpu(a->data.resident.value_offset) +
-                               attr_len, 0, from - attr_len);
-               a->data.resident.value_length = cpu_to_le32(from);
-               /* Zero the corresponding area in the page as well. */
-               if (PageUptodate(page)) {
-                       kaddr = kmap_atomic(page, KM_USER0);
-                       memset(kaddr + attr_len, 0, from - attr_len);
-                       kunmap_atomic(kaddr, KM_USER0);
-                       flush_dcache_page(page);
-               }
-       }
-       flush_dcache_mft_record_page(ctx->ntfs_ino);
-       mark_mft_record_dirty(ctx->ntfs_ino);
-done_unm:
-       ntfs_attr_put_search_ctx(ctx);
-       unmap_mft_record(base_ni);
-       /*
-        * Because resident attributes are handled by memcpy() to/from the
-        * corresponding MFT record, and because this form of i/o is byte
-        * aligned rather than block aligned, there is no need to bring the
-        * page uptodate here as in the non-resident case where we need to
-        * bring the buffers straddled by the write uptodate before
-        * generic_file_write() does the copying from userspace.
-        *
-        * We thus defer the uptodate bringing of the page region outside the
-        * region written to to ntfs_commit_write(), which makes the code
-        * simpler and saves one atomic kmap which is good.
-        */
-done:
-       ntfs_debug("Done.");
-       return 0;
-err_out:
-       if (err == -ENOMEM)
-               ntfs_warning(vi->i_sb, "Error allocating memory required to "
-                               "prepare the write.");
-       else {
-               ntfs_error(vi->i_sb, "Resident attribute prepare write failed "
-                               "with error %i.", err);
-               NVolSetErrors(vol);
-               make_bad_inode(vi);
-       }
-err_out2:
-       if (ctx)
-               ntfs_attr_put_search_ctx(ctx);
-       if (m)
-               unmap_mft_record(base_ni);
-       return err;
-}
-
-/**
- * ntfs_commit_nonresident_write -
- *
- */
-static int ntfs_commit_nonresident_write(struct page *page,
-               unsigned from, unsigned to)
-{
-       s64 pos = ((s64)page->index << PAGE_CACHE_SHIFT) + to;
-       struct inode *vi = page->mapping->host;
-       struct buffer_head *bh, *head;
-       unsigned int block_start, block_end, blocksize;
-       BOOL partial;
-
-       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
-                       "0x%lx, from = %u, to = %u.", vi->i_ino,
-                       NTFS_I(vi)->type, page->index, from, to);
-       blocksize = 1 << vi->i_blkbits;
-
-       // FIXME: We need a whole slew of special cases in here for compressed
-       // files for example...
-       // For now, we know ntfs_prepare_write() would have failed so we can't
-       // get here in any of the cases which we have to special case, so we
-       // are just a ripped off, unrolled generic_commit_write().
-
-       bh = head = page_buffers(page);
-       block_start = 0;
-       partial = FALSE;
-       do {
-               block_end = block_start + blocksize;
-               if (block_end <= from || block_start >= to) {
-                       if (!buffer_uptodate(bh))
-                               partial = TRUE;
-               } else {
-                       set_buffer_uptodate(bh);
-                       mark_buffer_dirty(bh);
-               }
-       } while (block_start = block_end, (bh = bh->b_this_page) != head);
-       /*
-        * If this is a partial write which happened to make all buffers
-        * uptodate then we can optimize away a bogus ->readpage() for the next
-        * read().  Here we 'discover' whether the page went uptodate as a
-        * result of this (potentially partial) write.
-        */
-       if (!partial)
-               SetPageUptodate(page);
-       /*
-        * Not convinced about this at all.  See disparity comment above.  For
-        * now we know ntfs_prepare_write() would have failed in the write
-        * exceeds i_size case, so this will never trigger which is fine.
-        */
-       if (pos > i_size_read(vi)) {
-               ntfs_error(vi->i_sb, "Writing beyond the existing file size is "
-                               "not supported yet.  Sorry.");
-               return -EOPNOTSUPP;
-               // vi->i_size = pos;
-               // mark_inode_dirty(vi);
-       }
-       ntfs_debug("Done.");
-       return 0;
-}
-
-/**
- * ntfs_commit_write - commit the received data
- *
- * This is called from generic_file_write() with i_sem held on the inode
- * (@page->mapping->host).  The @page is locked but not kmap()ped.  The source
- * data has already been copied into the @page.  ntfs_prepare_write() has been
- * called before the data copied and it returned success so we can take the
- * results of various BUG checks and some error handling for granted.
- *
- * Need to mark modified blocks dirty so they get written out later when
- * ntfs_writepage() is invoked by the VM.
- *
- * Return 0 on success or -errno on error.
- *
- * Should be using generic_commit_write().  This marks buffers uptodate and
- * dirty, sets the page uptodate if all buffers in the page are uptodate, and
- * updates i_size if the end of io is beyond i_size.  In that case, it also
- * marks the inode dirty.
- *
- * Cannot use generic_commit_write() due to ntfs specialities but can look at
- * it for implementation guidance.
- *
- * If things have gone as outlined in ntfs_prepare_write(), then we do not
- * need to do any page content modifications here at all, except in the write
- * to resident attribute case, where we need to do the uptodate bringing here
- * which we combine with the copying into the mft record which means we save
- * one atomic kmap.
- */
-static int ntfs_commit_write(struct file *file, struct page *page,
-               unsigned from, unsigned to)
-{
-       struct inode *vi = page->mapping->host;
-       ntfs_inode *base_ni, *ni = NTFS_I(vi);
-       char *kaddr, *kattr;
-       ntfs_attr_search_ctx *ctx;
-       MFT_RECORD *m;
-       ATTR_RECORD *a;
-       u32 attr_len;
-       int err;
-
-       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, page index "
-                       "0x%lx, from = %u, to = %u.", vi->i_ino, ni->type,
-                       page->index, from, to);
-       /* If the attribute is not resident, deal with it elsewhere. */
-       if (NInoNonResident(ni)) {
-               /* Only unnamed $DATA attributes can be compressed/encrypted. */
-               if (ni->type == AT_DATA && !ni->name_len) {
-                       /* Encrypted files need separate handling. */
-                       if (NInoEncrypted(ni)) {
-                               // We never get here at present!
-                               BUG();
-                       }
-                       /* Compressed data streams are handled in compress.c. */
-                       if (NInoCompressed(ni)) {
-                               // TODO: Implement this!
-                               // return ntfs_write_compressed_block(page);
-                               // We never get here at present!
-                               BUG();
-                       }
-               }
-               /* Normal data stream. */
-               return ntfs_commit_nonresident_write(page, from, to);
-       }
-       /*
-        * Attribute is resident, implying it is not compressed, encrypted, or
-        * sparse.
-        */
-       if (!NInoAttr(ni))
-               base_ni = ni;
-       else
-               base_ni = ni->ext.base_ntfs_ino;
-       /* Map, pin, and lock the mft record. */
-       m = map_mft_record(base_ni);
-       if (IS_ERR(m)) {
-               err = PTR_ERR(m);
-               m = NULL;
-               ctx = NULL;
-               goto err_out;
-       }
-       ctx = ntfs_attr_get_search_ctx(base_ni, m);
-       if (unlikely(!ctx)) {
-               err = -ENOMEM;
-               goto err_out;
-       }
-       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
-                       CASE_SENSITIVE, 0, NULL, 0, ctx);
-       if (unlikely(err)) {
-               if (err == -ENOENT)
-                       err = -EIO;
-               goto err_out;
-       }
-       a = ctx->attr;
-       /* The total length of the attribute value. */
-       attr_len = le32_to_cpu(a->data.resident.value_length);
-       BUG_ON(from > attr_len);
-       kattr = (u8*)a + le16_to_cpu(a->data.resident.value_offset);
-       kaddr = kmap_atomic(page, KM_USER0);
-       /* Copy the received data from the page to the mft record. */
-       memcpy(kattr + from, kaddr + from, to - from);
-       /* Update the attribute length if necessary. */
-       if (to > attr_len) {
-               attr_len = to;
-               a->data.resident.value_length = cpu_to_le32(attr_len);
-       }
-       /*
-        * If the page is not uptodate, bring the out of bounds area(s)
-        * uptodate by copying data from the mft record to the page.
-        */
-       if (!PageUptodate(page)) {
-               if (from > 0)
-                       memcpy(kaddr, kattr, from);
-               if (to < attr_len)
-                       memcpy(kaddr + to, kattr + to, attr_len - to);
-               /* Zero the region outside the end of the attribute value. */
-               if (attr_len < PAGE_CACHE_SIZE)
-                       memset(kaddr + attr_len, 0, PAGE_CACHE_SIZE - attr_len);
-               /*
-                * The probability of not having done any of the above is
-                * extremely small, so we just flush unconditionally.
-                */
-               flush_dcache_page(page);
-               SetPageUptodate(page);
-       }
-       kunmap_atomic(kaddr, KM_USER0);
-       /* Update i_size if necessary. */
-       if (i_size_read(vi) < attr_len) {
-               unsigned long flags;
-
-               write_lock_irqsave(&ni->size_lock, flags);
-               ni->allocated_size = ni->initialized_size = attr_len;
-               i_size_write(vi, attr_len);
-               write_unlock_irqrestore(&ni->size_lock, flags);
-       }
-       /* Mark the mft record dirty, so it gets written back. */
-       flush_dcache_mft_record_page(ctx->ntfs_ino);
-       mark_mft_record_dirty(ctx->ntfs_ino);
-       ntfs_attr_put_search_ctx(ctx);
-       unmap_mft_record(base_ni);
-       ntfs_debug("Done.");
-       return 0;
-err_out:
-       if (err == -ENOMEM) {
-               ntfs_warning(vi->i_sb, "Error allocating memory required to "
-                               "commit the write.");
-               if (PageUptodate(page)) {
-                       ntfs_warning(vi->i_sb, "Page is uptodate, setting "
-                                       "dirty so the write will be retried "
-                                       "later on by the VM.");
-                       /*
-                        * Put the page on mapping->dirty_pages, but leave its
-                        * buffers' dirty state as-is.
-                        */
-                       __set_page_dirty_nobuffers(page);
-                       err = 0;
-               } else
-                       ntfs_error(vi->i_sb, "Page is not uptodate.  Written "
-                                       "data has been lost.");
-       } else {
-               ntfs_error(vi->i_sb, "Resident attribute commit write failed "
-                               "with error %i.", err);
-               NVolSetErrors(ni->vol);
-               make_bad_inode(vi);
-       }
-       if (ctx)
-               ntfs_attr_put_search_ctx(ctx);
-       if (m)
-               unmap_mft_record(base_ni);
-       return err;
-}
-
 #endif /* NTFS_RW */
 
 /**
@@ -2377,9 +1552,6 @@ struct address_space_operations ntfs_aops = {
                                                   disk request queue. */
 #ifdef NTFS_RW
        .writepage      = ntfs_writepage,       /* Write dirty page to disk. */
-       .prepare_write  = ntfs_prepare_write,   /* Prepare page and buffers
-                                                  ready to receive data. */
-       .commit_write   = ntfs_commit_write,    /* Commit received data. */
 #endif /* NTFS_RW */
 };
 
index 3f9a4ff42ee51b07b75e57691997087df11a181d..eda056bac2567a51c99ef521347e066ac04f86a5 100644 (file)
@@ -21,7 +21,9 @@
  */
 
 #include <linux/buffer_head.h>
+#include <linux/sched.h>
 #include <linux/swap.h>
+#include <linux/writeback.h>
 
 #include "attrib.h"
 #include "debug.h"
  * ntfs_map_runlist_nolock - map (a part of) a runlist of an ntfs inode
  * @ni:                ntfs inode for which to map (part of) a runlist
  * @vcn:       map runlist part containing this vcn
+ * @ctx:       active attribute search context if present or NULL if not
  *
  * Map the part of a runlist containing the @vcn of the ntfs inode @ni.
  *
+ * If @ctx is specified, it is an active search context of @ni and its base mft
+ * record.  This is needed when ntfs_map_runlist_nolock() encounters unmapped
+ * runlist fragments and allows their mapping.  If you do not have the mft
+ * record mapped, you can specify @ctx as NULL and ntfs_map_runlist_nolock()
+ * will perform the necessary mapping and unmapping.
+ *
+ * Note, ntfs_map_runlist_nolock() saves the state of @ctx on entry and
+ * restores it before returning.  Thus, @ctx will be left pointing to the same
+ * attribute on return as on entry.  However, the actual pointers in @ctx may
+ * point to different memory locations on return, so you must remember to reset
+ * any cached pointers from the @ctx, i.e. after the call to
+ * ntfs_map_runlist_nolock(), you will probably want to do:
+ *     m = ctx->mrec;
+ *     a = ctx->attr;
+ * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that
+ * you cache ctx->mrec in a variable @m of type MFT_RECORD *.
+ *
  * Return 0 on success and -errno on error.  There is one special error code
  * which is not an error as such.  This is -ENOENT.  It means that @vcn is out
  * of bounds of the runlist.
  * Note the runlist can be NULL after this function returns if @vcn is zero and
  * the attribute has zero allocated size, i.e. there simply is no runlist.
  *
- * Locking: - The runlist must be locked for writing.
- *         - This function modifies the runlist.
+ * WARNING: If @ctx is supplied, regardless of whether success or failure is
+ *         returned, you need to check IS_ERR(@ctx->mrec) and if TRUE the @ctx
+ *         is no longer valid, i.e. you need to either call
+ *         ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it.
+ *         In that case PTR_ERR(@ctx->mrec) will give you the error code for
+ *         why the mapping of the old inode failed.
+ *
+ * Locking: - The runlist described by @ni must be locked for writing on entry
+ *           and is locked on return.  Note the runlist will be modified.
+ *         - If @ctx is NULL, the base mft record of @ni must not be mapped on
+ *           entry and it will be left unmapped on return.
+ *         - If @ctx is not NULL, the base mft record must be mapped on entry
+ *           and it will be left mapped on return.
  */
-int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn)
+int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn, ntfs_attr_search_ctx *ctx)
 {
        VCN end_vcn;
+       unsigned long flags;
        ntfs_inode *base_ni;
        MFT_RECORD *m;
        ATTR_RECORD *a;
-       ntfs_attr_search_ctx *ctx;
        runlist_element *rl;
-       unsigned long flags;
+       struct page *put_this_page = NULL;
        int err = 0;
+       BOOL ctx_is_temporary, ctx_needs_reset;
+       ntfs_attr_search_ctx old_ctx = { NULL, };
 
        ntfs_debug("Mapping runlist part containing vcn 0x%llx.",
                        (unsigned long long)vcn);
@@ -66,20 +99,77 @@ int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn)
                base_ni = ni;
        else
                base_ni = ni->ext.base_ntfs_ino;
-       m = map_mft_record(base_ni);
-       if (IS_ERR(m))
-               return PTR_ERR(m);
-       ctx = ntfs_attr_get_search_ctx(base_ni, m);
-       if (unlikely(!ctx)) {
-               err = -ENOMEM;
-               goto err_out;
+       if (!ctx) {
+               ctx_is_temporary = ctx_needs_reset = TRUE;
+               m = map_mft_record(base_ni);
+               if (IS_ERR(m))
+                       return PTR_ERR(m);
+               ctx = ntfs_attr_get_search_ctx(base_ni, m);
+               if (unlikely(!ctx)) {
+                       err = -ENOMEM;
+                       goto err_out;
+               }
+       } else {
+               VCN allocated_size_vcn;
+
+               BUG_ON(IS_ERR(ctx->mrec));
+               a = ctx->attr;
+               BUG_ON(!a->non_resident);
+               ctx_is_temporary = FALSE;
+               end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn);
+               read_lock_irqsave(&ni->size_lock, flags);
+               allocated_size_vcn = ni->allocated_size >>
+                               ni->vol->cluster_size_bits;
+               read_unlock_irqrestore(&ni->size_lock, flags);
+               if (!a->data.non_resident.lowest_vcn && end_vcn <= 0)
+                       end_vcn = allocated_size_vcn - 1;
+               /*
+                * If we already have the attribute extent containing @vcn in
+                * @ctx, no need to look it up again.  We slightly cheat in
+                * that if vcn exceeds the allocated size, we will refuse to
+                * map the runlist below, so there is definitely no need to get
+                * the right attribute extent.
+                */
+               if (vcn >= allocated_size_vcn || (a->type == ni->type &&
+                               a->name_length == ni->name_len &&
+                               !memcmp((u8*)a + le16_to_cpu(a->name_offset),
+                               ni->name, ni->name_len) &&
+                               sle64_to_cpu(a->data.non_resident.lowest_vcn)
+                               <= vcn && end_vcn >= vcn))
+                       ctx_needs_reset = FALSE;
+               else {
+                       /* Save the old search context. */
+                       old_ctx = *ctx;
+                       /*
+                        * If the currently mapped (extent) inode is not the
+                        * base inode we will unmap it when we reinitialize the
+                        * search context which means we need to get a
+                        * reference to the page containing the mapped mft
+                        * record so we do not accidentally drop changes to the
+                        * mft record when it has not been marked dirty yet.
+                        */
+                       if (old_ctx.base_ntfs_ino && old_ctx.ntfs_ino !=
+                                       old_ctx.base_ntfs_ino) {
+                               put_this_page = old_ctx.ntfs_ino->page;
+                               page_cache_get(put_this_page);
+                       }
+                       /*
+                        * Reinitialize the search context so we can lookup the
+                        * needed attribute extent.
+                        */
+                       ntfs_attr_reinit_search_ctx(ctx);
+                       ctx_needs_reset = TRUE;
+               }
        }
-       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
-                       CASE_SENSITIVE, vcn, NULL, 0, ctx);
-       if (unlikely(err)) {
-               if (err == -ENOENT)
-                       err = -EIO;
-               goto err_out;
+       if (ctx_needs_reset) {
+               err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                               CASE_SENSITIVE, vcn, NULL, 0, ctx);
+               if (unlikely(err)) {
+                       if (err == -ENOENT)
+                               err = -EIO;
+                       goto err_out;
+               }
+               BUG_ON(!ctx->attr->non_resident);
        }
        a = ctx->attr;
        /*
@@ -89,11 +179,9 @@ int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn)
         * ntfs_mapping_pairs_decompress() fails.
         */
        end_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn) + 1;
-       if (unlikely(!a->data.non_resident.lowest_vcn && end_vcn <= 1)) {
-               read_lock_irqsave(&ni->size_lock, flags);
-               end_vcn = ni->allocated_size >> ni->vol->cluster_size_bits;
-               read_unlock_irqrestore(&ni->size_lock, flags);
-       }
+       if (!a->data.non_resident.lowest_vcn && end_vcn == 1)
+               end_vcn = sle64_to_cpu(a->data.non_resident.allocated_size) >>
+                               ni->vol->cluster_size_bits;
        if (unlikely(vcn >= end_vcn)) {
                err = -ENOENT;
                goto err_out;
@@ -104,9 +192,93 @@ int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn)
        else
                ni->runlist.rl = rl;
 err_out:
-       if (likely(ctx))
-               ntfs_attr_put_search_ctx(ctx);
-       unmap_mft_record(base_ni);
+       if (ctx_is_temporary) {
+               if (likely(ctx))
+                       ntfs_attr_put_search_ctx(ctx);
+               unmap_mft_record(base_ni);
+       } else if (ctx_needs_reset) {
+               /*
+                * If there is no attribute list, restoring the search context
+                * is acomplished simply by copying the saved context back over
+                * the caller supplied context.  If there is an attribute list,
+                * things are more complicated as we need to deal with mapping
+                * of mft records and resulting potential changes in pointers.
+                */
+               if (NInoAttrList(base_ni)) {
+                       /*
+                        * If the currently mapped (extent) inode is not the
+                        * one we had before, we need to unmap it and map the
+                        * old one.
+                        */
+                       if (ctx->ntfs_ino != old_ctx.ntfs_ino) {
+                               /*
+                                * If the currently mapped inode is not the
+                                * base inode, unmap it.
+                                */
+                               if (ctx->base_ntfs_ino && ctx->ntfs_ino !=
+                                               ctx->base_ntfs_ino) {
+                                       unmap_extent_mft_record(ctx->ntfs_ino);
+                                       ctx->mrec = ctx->base_mrec;
+                                       BUG_ON(!ctx->mrec);
+                               }
+                               /*
+                                * If the old mapped inode is not the base
+                                * inode, map it.
+                                */
+                               if (old_ctx.base_ntfs_ino &&
+                                               old_ctx.ntfs_ino !=
+                                               old_ctx.base_ntfs_ino) {
+retry_map:
+                                       ctx->mrec = map_mft_record(
+                                                       old_ctx.ntfs_ino);
+                                       /*
+                                        * Something bad has happened.  If out
+                                        * of memory retry till it succeeds.
+                                        * Any other errors are fatal and we
+                                        * return the error code in ctx->mrec.
+                                        * Let the caller deal with it...  We
+                                        * just need to fudge things so the
+                                        * caller can reinit and/or put the
+                                        * search context safely.
+                                        */
+                                       if (IS_ERR(ctx->mrec)) {
+                                               if (PTR_ERR(ctx->mrec) ==
+                                                               -ENOMEM) {
+                                                       schedule();
+                                                       goto retry_map;
+                                               } else
+                                                       old_ctx.ntfs_ino =
+                                                               old_ctx.
+                                                               base_ntfs_ino;
+                                       }
+                               }
+                       }
+                       /* Update the changed pointers in the saved context. */
+                       if (ctx->mrec != old_ctx.mrec) {
+                               if (!IS_ERR(ctx->mrec))
+                                       old_ctx.attr = (ATTR_RECORD*)(
+                                                       (u8*)ctx->mrec +
+                                                       ((u8*)old_ctx.attr -
+                                                       (u8*)old_ctx.mrec));
+                               old_ctx.mrec = ctx->mrec;
+                       }
+               }
+               /* Restore the search context to the saved one. */
+               *ctx = old_ctx;
+               /*
+                * We drop the reference on the page we took earlier.  In the
+                * case that IS_ERR(ctx->mrec) is true this means we might lose
+                * some changes to the mft record that had been made between
+                * the last time it was marked dirty/written out and now.  This
+                * at this stage is not a problem as the mapping error is fatal
+                * enough that the mft record cannot be written out anyway and
+                * the caller is very likely to shutdown the whole inode
+                * immediately and mark the volume dirty for chkdsk to pick up
+                * the pieces anyway.
+                */
+               if (put_this_page)
+                       page_cache_release(put_this_page);
+       }
        return err;
 }
 
@@ -122,8 +294,8 @@ err_out:
  * of bounds of the runlist.
  *
  * Locking: - The runlist must be unlocked on entry and is unlocked on return.
- *         - This function takes the runlist lock for writing and modifies the
- *           runlist.
+ *         - This function takes the runlist lock for writing and may modify
+ *           the runlist.
  */
 int ntfs_map_runlist(ntfs_inode *ni, VCN vcn)
 {
@@ -133,7 +305,7 @@ int ntfs_map_runlist(ntfs_inode *ni, VCN vcn)
        /* Make sure someone else didn't do the work while we were sleeping. */
        if (likely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) <=
                        LCN_RL_NOT_MAPPED))
-               err = ntfs_map_runlist_nolock(ni, vcn);
+               err = ntfs_map_runlist_nolock(ni, vcn, NULL);
        up_write(&ni->runlist.lock);
        return err;
 }
@@ -212,7 +384,7 @@ retry_remap:
                                goto retry_remap;
                        }
                }
-               err = ntfs_map_runlist_nolock(ni, vcn);
+               err = ntfs_map_runlist_nolock(ni, vcn, NULL);
                if (!write_locked) {
                        up_write(&ni->runlist.lock);
                        down_read(&ni->runlist.lock);
@@ -236,9 +408,9 @@ retry_remap:
 
 /**
  * ntfs_attr_find_vcn_nolock - find a vcn in the runlist of an ntfs inode
- * @ni:                        ntfs inode describing the runlist to search
- * @vcn:               vcn to find
- * @write_locked:      true if the runlist is locked for writing
+ * @ni:                ntfs inode describing the runlist to search
+ * @vcn:       vcn to find
+ * @ctx:       active attribute search context if present or NULL if not
  *
  * Find the virtual cluster number @vcn in the runlist described by the ntfs
  * inode @ni and return the address of the runlist element containing the @vcn.
@@ -246,9 +418,22 @@ retry_remap:
  * If the @vcn is not mapped yet, the attempt is made to map the attribute
  * extent containing the @vcn and the vcn to lcn conversion is retried.
  *
- * If @write_locked is true the caller has locked the runlist for writing and
- * if false for reading.
- *
+ * If @ctx is specified, it is an active search context of @ni and its base mft
+ * record.  This is needed when ntfs_attr_find_vcn_nolock() encounters unmapped
+ * runlist fragments and allows their mapping.  If you do not have the mft
+ * record mapped, you can specify @ctx as NULL and ntfs_attr_find_vcn_nolock()
+ * will perform the necessary mapping and unmapping.
+ *
+ * Note, ntfs_attr_find_vcn_nolock() saves the state of @ctx on entry and
+ * restores it before returning.  Thus, @ctx will be left pointing to the same
+ * attribute on return as on entry.  However, the actual pointers in @ctx may
+ * point to different memory locations on return, so you must remember to reset
+ * any cached pointers from the @ctx, i.e. after the call to
+ * ntfs_attr_find_vcn_nolock(), you will probably want to do:
+ *     m = ctx->mrec;
+ *     a = ctx->attr;
+ * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that
+ * you cache ctx->mrec in a variable @m of type MFT_RECORD *.
  * Note you need to distinguish between the lcn of the returned runlist element
  * being >= 0 and LCN_HOLE.  In the later case you have to return zeroes on
  * read and allocate clusters on write.
@@ -263,22 +448,31 @@ retry_remap:
  *     -ENOMEM - Not enough memory to map runlist.
  *     -EIO    - Critical error (runlist/file is corrupt, i/o error, etc).
  *
- * Locking: - The runlist must be locked on entry and is left locked on return.
- *         - If @write_locked is FALSE, i.e. the runlist is locked for reading,
- *           the lock may be dropped inside the function so you cannot rely on
- *           the runlist still being the same when this function returns.
+ * WARNING: If @ctx is supplied, regardless of whether success or failure is
+ *         returned, you need to check IS_ERR(@ctx->mrec) and if TRUE the @ctx
+ *         is no longer valid, i.e. you need to either call
+ *         ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it.
+ *         In that case PTR_ERR(@ctx->mrec) will give you the error code for
+ *         why the mapping of the old inode failed.
+ *
+ * Locking: - The runlist described by @ni must be locked for writing on entry
+ *           and is locked on return.  Note the runlist may be modified when
+ *           needed runlist fragments need to be mapped.
+ *         - If @ctx is NULL, the base mft record of @ni must not be mapped on
+ *           entry and it will be left unmapped on return.
+ *         - If @ctx is not NULL, the base mft record must be mapped on entry
+ *           and it will be left mapped on return.
  */
 runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni, const VCN vcn,
-               const BOOL write_locked)
+               ntfs_attr_search_ctx *ctx)
 {
        unsigned long flags;
        runlist_element *rl;
        int err = 0;
        BOOL is_retry = FALSE;
 
-       ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, %s_locked.",
-                       ni->mft_no, (unsigned long long)vcn,
-                       write_locked ? "write" : "read");
+       ntfs_debug("Entering for i_ino 0x%lx, vcn 0x%llx, with%s ctx.",
+                       ni->mft_no, (unsigned long long)vcn, ctx ? "" : "out");
        BUG_ON(!ni);
        BUG_ON(!NInoNonResident(ni));
        BUG_ON(vcn < 0);
@@ -312,33 +506,22 @@ retry_remap:
        }
        if (!err && !is_retry) {
                /*
-                * The @vcn is in an unmapped region, map the runlist and
-                * retry.
+                * If the search context is invalid we cannot map the unmapped
+                * region.
                 */
-               if (!write_locked) {
-                       up_read(&ni->runlist.lock);
-                       down_write(&ni->runlist.lock);
-                       if (unlikely(ntfs_rl_vcn_to_lcn(ni->runlist.rl, vcn) !=
-                                       LCN_RL_NOT_MAPPED)) {
-                               up_write(&ni->runlist.lock);
-                               down_read(&ni->runlist.lock);
+               if (IS_ERR(ctx->mrec))
+                       err = PTR_ERR(ctx->mrec);
+               else {
+                       /*
+                        * The @vcn is in an unmapped region, map the runlist
+                        * and retry.
+                        */
+                       err = ntfs_map_runlist_nolock(ni, vcn, ctx);
+                       if (likely(!err)) {
+                               is_retry = TRUE;
                                goto retry_remap;
                        }
                }
-               err = ntfs_map_runlist_nolock(ni, vcn);
-               if (!write_locked) {
-                       up_write(&ni->runlist.lock);
-                       down_read(&ni->runlist.lock);
-               }
-               if (likely(!err)) {
-                       is_retry = TRUE;
-                       goto retry_remap;
-               }
-               /*
-                * -EINVAL coming from a failed mapping attempt is equivalent
-                * to i/o error for us as it should not happen in our code
-                * paths.
-                */
                if (err == -EINVAL)
                        err = -EIO;
        } else if (!err)
@@ -1011,6 +1194,7 @@ int ntfs_attr_lookup(const ATTR_TYPE type, const ntfschar *name,
        ntfs_inode *base_ni;
 
        ntfs_debug("Entering.");
+       BUG_ON(IS_ERR(ctx->mrec));
        if (ctx->base_ntfs_ino)
                base_ni = ctx->base_ntfs_ino;
        else
@@ -1227,7 +1411,7 @@ int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPE type)
  */
 int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPE type)
 {
-       if (type == AT_INDEX_ALLOCATION || type == AT_EA)
+       if (type == AT_INDEX_ALLOCATION)
                return -EPERM;
        return 0;
 }
@@ -1319,10 +1503,17 @@ int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
 /**
  * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute
  * @ni:                ntfs inode describing the attribute to convert
+ * @data_size: size of the resident data to copy to the non-resident attribute
  *
  * Convert the resident ntfs attribute described by the ntfs inode @ni to a
  * non-resident one.
  *
+ * @data_size must be equal to the attribute value size.  This is needed since
+ * we need to know the size before we can map the mft record and our callers
+ * always know it.  The reason we cannot simply read the size from the vfs
+ * inode i_size is that this is not necessarily uptodate.  This happens when
+ * ntfs_attr_make_non_resident() is called in the ->truncate call path(s).
+ *
  * Return 0 on success and -errno on error.  The following error return codes
  * are defined:
  *     -EPERM  - The attribute is not allowed to be non-resident.
@@ -1343,7 +1534,7 @@ int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
  *
  * Locking: - The caller must hold i_sem on the inode.
  */
-int ntfs_attr_make_non_resident(ntfs_inode *ni)
+int ntfs_attr_make_non_resident(ntfs_inode *ni, const u32 data_size)
 {
        s64 new_size;
        struct inode *vi = VFS_I(ni);
@@ -1381,11 +1572,9 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
         * The size needs to be aligned to a cluster boundary for allocation
         * purposes.
         */
-       new_size = (i_size_read(vi) + vol->cluster_size - 1) &
+       new_size = (data_size + vol->cluster_size - 1) &
                        ~(vol->cluster_size - 1);
        if (new_size > 0) {
-               runlist_element *rl2;
-
                /*
                 * Will need the page later and since the page lock nests
                 * outside all ntfs locks, we need to get the page now.
@@ -1396,7 +1585,7 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
                        return -ENOMEM;
                /* Start by allocating clusters to hold the attribute value. */
                rl = ntfs_cluster_alloc(vol, 0, new_size >>
-                               vol->cluster_size_bits, -1, DATA_ZONE);
+                               vol->cluster_size_bits, -1, DATA_ZONE, TRUE);
                if (IS_ERR(rl)) {
                        err = PTR_ERR(rl);
                        ntfs_debug("Failed to allocate cluster%s, error code "
@@ -1405,12 +1594,6 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
                                        err);
                        goto page_err_out;
                }
-               /* Change the runlist terminator to LCN_ENOENT. */
-               rl2 = rl;
-               while (rl2->length)
-                       rl2++;
-               BUG_ON(rl2->lcn != LCN_RL_NOT_MAPPED);
-               rl2->lcn = LCN_ENOENT;
        } else {
                rl = NULL;
                page = NULL;
@@ -1473,7 +1656,7 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
         * attribute value.
         */
        attr_size = le32_to_cpu(a->data.resident.value_length);
-       BUG_ON(attr_size != i_size_read(vi));
+       BUG_ON(attr_size != data_size);
        if (page && !PageUptodate(page)) {
                kaddr = kmap_atomic(page, KM_USER0);
                memcpy(kaddr, (u8*)a +
@@ -1538,7 +1721,9 @@ int ntfs_attr_make_non_resident(ntfs_inode *ni)
                                ffs(ni->itype.compressed.block_size) - 1;
                ni->itype.compressed.block_clusters = 1U <<
                                a->data.non_resident.compression_unit;
-       }
+               vi->i_blocks = ni->itype.compressed.size >> 9;
+       } else
+               vi->i_blocks = ni->allocated_size >> 9;
        write_unlock_irqrestore(&ni->size_lock, flags);
        /*
         * This needs to be last since the address space operations ->readpage
@@ -1651,6 +1836,640 @@ page_err_out:
        return err;
 }
 
+/**
+ * ntfs_attr_extend_allocation - extend the allocated space of an attribute
+ * @ni:                        ntfs inode of the attribute whose allocation to extend
+ * @new_alloc_size:    new size in bytes to which to extend the allocation to
+ * @new_data_size:     new size in bytes to which to extend the data to
+ * @data_start:                beginning of region which is required to be non-sparse
+ *
+ * Extend the allocated space of an attribute described by the ntfs inode @ni
+ * to @new_alloc_size bytes.  If @data_start is -1, the whole extension may be
+ * implemented as a hole in the file (as long as both the volume and the ntfs
+ * inode @ni have sparse support enabled).  If @data_start is >= 0, then the
+ * region between the old allocated size and @data_start - 1 may be made sparse
+ * but the regions between @data_start and @new_alloc_size must be backed by
+ * actual clusters.
+ *
+ * If @new_data_size is -1, it is ignored.  If it is >= 0, then the data size
+ * of the attribute is extended to @new_data_size.  Note that the i_size of the
+ * vfs inode is not updated.  Only the data size in the base attribute record
+ * is updated.  The caller has to update i_size separately if this is required.
+ * WARNING: It is a BUG() for @new_data_size to be smaller than the old data
+ * size as well as for @new_data_size to be greater than @new_alloc_size.
+ *
+ * For resident attributes this involves resizing the attribute record and if
+ * necessary moving it and/or other attributes into extent mft records and/or
+ * converting the attribute to a non-resident attribute which in turn involves
+ * extending the allocation of a non-resident attribute as described below.
+ *
+ * For non-resident attributes this involves allocating clusters in the data
+ * zone on the volume (except for regions that are being made sparse) and
+ * extending the run list to describe the allocated clusters as well as
+ * updating the mapping pairs array of the attribute.  This in turn involves
+ * resizing the attribute record and if necessary moving it and/or other
+ * attributes into extent mft records and/or splitting the attribute record
+ * into multiple extent attribute records.
+ *
+ * Also, the attribute list attribute is updated if present and in some of the
+ * above cases (the ones where extent mft records/attributes come into play),
+ * an attribute list attribute is created if not already present.
+ *
+ * Return the new allocated size on success and -errno on error.  In the case
+ * that an error is encountered but a partial extension at least up to
+ * @data_start (if present) is possible, the allocation is partially extended
+ * and this is returned.  This means the caller must check the returned size to
+ * determine if the extension was partial.  If @data_start is -1 then partial
+ * allocations are not performed.
+ *
+ * WARNING: Do not call ntfs_attr_extend_allocation() for $MFT/$DATA.
+ *
+ * Locking: This function takes the runlist lock of @ni for writing as well as
+ * locking the mft record of the base ntfs inode.  These locks are maintained
+ * throughout execution of the function.  These locks are required so that the
+ * attribute can be resized safely and so that it can for example be converted
+ * from resident to non-resident safely.
+ *
+ * TODO: At present attribute list attribute handling is not implemented.
+ *
+ * TODO: At present it is not safe to call this function for anything other
+ * than the $DATA attribute(s) of an uncompressed and unencrypted file.
+ */
+s64 ntfs_attr_extend_allocation(ntfs_inode *ni, s64 new_alloc_size,
+               const s64 new_data_size, const s64 data_start)
+{
+       VCN vcn;
+       s64 ll, allocated_size, start = data_start;
+       struct inode *vi = VFS_I(ni);
+       ntfs_volume *vol = ni->vol;
+       ntfs_inode *base_ni;
+       MFT_RECORD *m;
+       ATTR_RECORD *a;
+       ntfs_attr_search_ctx *ctx;
+       runlist_element *rl, *rl2;
+       unsigned long flags;
+       int err, mp_size;
+       u32 attr_len = 0; /* Silence stupid gcc warning. */
+       BOOL mp_rebuilt;
+
+#ifdef NTFS_DEBUG
+       read_lock_irqsave(&ni->size_lock, flags);
+       allocated_size = ni->allocated_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       ntfs_debug("Entering for i_ino 0x%lx, attribute type 0x%x, "
+                       "old_allocated_size 0x%llx, "
+                       "new_allocated_size 0x%llx, new_data_size 0x%llx, "
+                       "data_start 0x%llx.", vi->i_ino,
+                       (unsigned)le32_to_cpu(ni->type),
+                       (unsigned long long)allocated_size,
+                       (unsigned long long)new_alloc_size,
+                       (unsigned long long)new_data_size,
+                       (unsigned long long)start);
+#endif
+retry_extend:
+       /*
+        * For non-resident attributes, @start and @new_size need to be aligned
+        * to cluster boundaries for allocation purposes.
+        */
+       if (NInoNonResident(ni)) {
+               if (start > 0)
+                       start &= ~(s64)vol->cluster_size_mask;
+               new_alloc_size = (new_alloc_size + vol->cluster_size - 1) &
+                               ~(s64)vol->cluster_size_mask;
+       }
+       BUG_ON(new_data_size >= 0 && new_data_size > new_alloc_size);
+       /* Check if new size is allowed in $AttrDef. */
+       err = ntfs_attr_size_bounds_check(vol, ni->type, new_alloc_size);
+       if (unlikely(err)) {
+               /* Only emit errors when the write will fail completely. */
+               read_lock_irqsave(&ni->size_lock, flags);
+               allocated_size = ni->allocated_size;
+               read_unlock_irqrestore(&ni->size_lock, flags);
+               if (start < 0 || start >= allocated_size) {
+                       if (err == -ERANGE) {
+                               ntfs_error(vol->sb, "Cannot extend allocation "
+                                               "of inode 0x%lx, attribute "
+                                               "type 0x%x, because the new "
+                                               "allocation would exceed the "
+                                               "maximum allowed size for "
+                                               "this attribute type.",
+                                               vi->i_ino, (unsigned)
+                                               le32_to_cpu(ni->type));
+                       } else {
+                               ntfs_error(vol->sb, "Cannot extend allocation "
+                                               "of inode 0x%lx, attribute "
+                                               "type 0x%x, because this "
+                                               "attribute type is not "
+                                               "defined on the NTFS volume.  "
+                                               "Possible corruption!  You "
+                                               "should run chkdsk!",
+                                               vi->i_ino, (unsigned)
+                                               le32_to_cpu(ni->type));
+                       }
+               }
+               /* Translate error code to be POSIX conformant for write(2). */
+               if (err == -ERANGE)
+                       err = -EFBIG;
+               else
+                       err = -EIO;
+               return err;
+       }
+       if (!NInoAttr(ni))
+               base_ni = ni;
+       else
+               base_ni = ni->ext.base_ntfs_ino;
+       /*
+        * We will be modifying both the runlist (if non-resident) and the mft
+        * record so lock them both down.
+        */
+       down_write(&ni->runlist.lock);
+       m = map_mft_record(base_ni);
+       if (IS_ERR(m)) {
+               err = PTR_ERR(m);
+               m = NULL;
+               ctx = NULL;
+               goto err_out;
+       }
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
+       if (unlikely(!ctx)) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+       read_lock_irqsave(&ni->size_lock, flags);
+       allocated_size = ni->allocated_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       /*
+        * If non-resident, seek to the last extent.  If resident, there is
+        * only one extent, so seek to that.
+        */
+       vcn = NInoNonResident(ni) ? allocated_size >> vol->cluster_size_bits :
+                       0;
+       /*
+        * Abort if someone did the work whilst we waited for the locks.  If we
+        * just converted the attribute from resident to non-resident it is
+        * likely that exactly this has happened already.  We cannot quite
+        * abort if we need to update the data size.
+        */
+       if (unlikely(new_alloc_size <= allocated_size)) {
+               ntfs_debug("Allocated size already exceeds requested size.");
+               new_alloc_size = allocated_size;
+               if (new_data_size < 0)
+                       goto done;
+               /*
+                * We want the first attribute extent so that we can update the
+                * data size.
+                */
+               vcn = 0;
+       }
+       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                       CASE_SENSITIVE, vcn, NULL, 0, ctx);
+       if (unlikely(err)) {
+               if (err == -ENOENT)
+                       err = -EIO;
+               goto err_out;
+       }
+       m = ctx->mrec;
+       a = ctx->attr;
+       /* Use goto to reduce indentation. */
+       if (a->non_resident)
+               goto do_non_resident_extend;
+       BUG_ON(NInoNonResident(ni));
+       /* The total length of the attribute value. */
+       attr_len = le32_to_cpu(a->data.resident.value_length);
+       /*
+        * Extend the attribute record to be able to store the new attribute
+        * size.  ntfs_attr_record_resize() will not do anything if the size is
+        * not changing.
+        */
+       if (new_alloc_size < vol->mft_record_size &&
+                       !ntfs_attr_record_resize(m, a,
+                       le16_to_cpu(a->data.resident.value_offset) +
+                       new_alloc_size)) {
+               /* The resize succeeded! */
+               write_lock_irqsave(&ni->size_lock, flags);
+               ni->allocated_size = le32_to_cpu(a->length) -
+                               le16_to_cpu(a->data.resident.value_offset);
+               write_unlock_irqrestore(&ni->size_lock, flags);
+               if (new_data_size >= 0) {
+                       BUG_ON(new_data_size < attr_len);
+                       a->data.resident.value_length =
+                                       cpu_to_le32((u32)new_data_size);
+               }
+               goto flush_done;
+       }
+       /*
+        * We have to drop all the locks so we can call
+        * ntfs_attr_make_non_resident().  This could be optimised by try-
+        * locking the first page cache page and only if that fails dropping
+        * the locks, locking the page, and redoing all the locking and
+        * lookups.  While this would be a huge optimisation, it is not worth
+        * it as this is definitely a slow code path.
+        */
+       ntfs_attr_put_search_ctx(ctx);
+       unmap_mft_record(base_ni);
+       up_write(&ni->runlist.lock);
+       /*
+        * Not enough space in the mft record, try to make the attribute
+        * non-resident and if successful restart the extension process.
+        */
+       err = ntfs_attr_make_non_resident(ni, attr_len);
+       if (likely(!err))
+               goto retry_extend;
+       /*
+        * Could not make non-resident.  If this is due to this not being
+        * permitted for this attribute type or there not being enough space,
+        * try to make other attributes non-resident.  Otherwise fail.
+        */
+       if (unlikely(err != -EPERM && err != -ENOSPC)) {
+               /* Only emit errors when the write will fail completely. */
+               read_lock_irqsave(&ni->size_lock, flags);
+               allocated_size = ni->allocated_size;
+               read_unlock_irqrestore(&ni->size_lock, flags);
+               if (start < 0 || start >= allocated_size)
+                       ntfs_error(vol->sb, "Cannot extend allocation of "
+                                       "inode 0x%lx, attribute type 0x%x, "
+                                       "because the conversion from resident "
+                                       "to non-resident attribute failed "
+                                       "with error code %i.", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+               if (err != -ENOMEM)
+                       err = -EIO;
+               goto conv_err_out;
+       }
+       /* TODO: Not implemented from here, abort. */
+       read_lock_irqsave(&ni->size_lock, flags);
+       allocated_size = ni->allocated_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       if (start < 0 || start >= allocated_size) {
+               if (err == -ENOSPC)
+                       ntfs_error(vol->sb, "Not enough space in the mft "
+                                       "record/on disk for the non-resident "
+                                       "attribute value.  This case is not "
+                                       "implemented yet.");
+               else /* if (err == -EPERM) */
+                       ntfs_error(vol->sb, "This attribute type may not be "
+                                       "non-resident.  This case is not "
+                                       "implemented yet.");
+       }
+       err = -EOPNOTSUPP;
+       goto conv_err_out;
+#if 0
+       // TODO: Attempt to make other attributes non-resident.
+       if (!err)
+               goto do_resident_extend;
+       /*
+        * Both the attribute list attribute and the standard information
+        * attribute must remain in the base inode.  Thus, if this is one of
+        * these attributes, we have to try to move other attributes out into
+        * extent mft records instead.
+        */
+       if (ni->type == AT_ATTRIBUTE_LIST ||
+                       ni->type == AT_STANDARD_INFORMATION) {
+               // TODO: Attempt to move other attributes into extent mft
+               // records.
+               err = -EOPNOTSUPP;
+               if (!err)
+                       goto do_resident_extend;
+               goto err_out;
+       }
+       // TODO: Attempt to move this attribute to an extent mft record, but
+       // only if it is not already the only attribute in an mft record in
+       // which case there would be nothing to gain.
+       err = -EOPNOTSUPP;
+       if (!err)
+               goto do_resident_extend;
+       /* There is nothing we can do to make enough space. )-: */
+       goto err_out;
+#endif
+do_non_resident_extend:
+       BUG_ON(!NInoNonResident(ni));
+       if (new_alloc_size == allocated_size) {
+               BUG_ON(vcn);
+               goto alloc_done;
+       }
+       /*
+        * If the data starts after the end of the old allocation, this is a
+        * $DATA attribute and sparse attributes are enabled on the volume and
+        * for this inode, then create a sparse region between the old
+        * allocated size and the start of the data.  Otherwise simply proceed
+        * with filling the whole space between the old allocated size and the
+        * new allocated size with clusters.
+        */
+       if ((start >= 0 && start <= allocated_size) || ni->type != AT_DATA ||
+                       !NVolSparseEnabled(vol) || NInoSparseDisabled(ni))
+               goto skip_sparse;
+       // TODO: This is not implemented yet.  We just fill in with real
+       // clusters for now...
+       ntfs_debug("Inserting holes is not-implemented yet.  Falling back to "
+                       "allocating real clusters instead.");
+skip_sparse:
+       rl = ni->runlist.rl;
+       if (likely(rl)) {
+               /* Seek to the end of the runlist. */
+               while (rl->length)
+                       rl++;
+       }
+       /* If this attribute extent is not mapped, map it now. */
+       if (unlikely(!rl || rl->lcn == LCN_RL_NOT_MAPPED ||
+                       (rl->lcn == LCN_ENOENT && rl > ni->runlist.rl &&
+                       (rl-1)->lcn == LCN_RL_NOT_MAPPED))) {
+               if (!rl && !allocated_size)
+                       goto first_alloc;
+               rl = ntfs_mapping_pairs_decompress(vol, a, ni->runlist.rl);
+               if (IS_ERR(rl)) {
+                       err = PTR_ERR(rl);
+                       if (start < 0 || start >= allocated_size)
+                               ntfs_error(vol->sb, "Cannot extend allocation "
+                                               "of inode 0x%lx, attribute "
+                                               "type 0x%x, because the "
+                                               "mapping of a runlist "
+                                               "fragment failed with error "
+                                               "code %i.", vi->i_ino,
+                                               (unsigned)le32_to_cpu(ni->type),
+                                               err);
+                       if (err != -ENOMEM)
+                               err = -EIO;
+                       goto err_out;
+               }
+               ni->runlist.rl = rl;
+               /* Seek to the end of the runlist. */
+               while (rl->length)
+                       rl++;
+       }
+       /*
+        * We now know the runlist of the last extent is mapped and @rl is at
+        * the end of the runlist.  We want to begin allocating clusters
+        * starting at the last allocated cluster to reduce fragmentation.  If
+        * there are no valid LCNs in the attribute we let the cluster
+        * allocator choose the starting cluster.
+        */
+       /* If the last LCN is a hole or simillar seek back to last real LCN. */
+       while (rl->lcn < 0 && rl > ni->runlist.rl)
+               rl--;
+first_alloc:
+       // FIXME: Need to implement partial allocations so at least part of the
+       // write can be performed when start >= 0.  (Needed for POSIX write(2)
+       // conformance.)
+       rl2 = ntfs_cluster_alloc(vol, allocated_size >> vol->cluster_size_bits,
+                       (new_alloc_size - allocated_size) >>
+                       vol->cluster_size_bits, (rl && (rl->lcn >= 0)) ?
+                       rl->lcn + rl->length : -1, DATA_ZONE, TRUE);
+       if (IS_ERR(rl2)) {
+               err = PTR_ERR(rl2);
+               if (start < 0 || start >= allocated_size)
+                       ntfs_error(vol->sb, "Cannot extend allocation of "
+                                       "inode 0x%lx, attribute type 0x%x, "
+                                       "because the allocation of clusters "
+                                       "failed with error code %i.", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+               if (err != -ENOMEM && err != -ENOSPC)
+                       err = -EIO;
+               goto err_out;
+       }
+       rl = ntfs_runlists_merge(ni->runlist.rl, rl2);
+       if (IS_ERR(rl)) {
+               err = PTR_ERR(rl);
+               if (start < 0 || start >= allocated_size)
+                       ntfs_error(vol->sb, "Cannot extend allocation of "
+                                       "inode 0x%lx, attribute type 0x%x, "
+                                       "because the runlist merge failed "
+                                       "with error code %i.", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+               if (err != -ENOMEM)
+                       err = -EIO;
+               if (ntfs_cluster_free_from_rl(vol, rl2)) {
+                       ntfs_error(vol->sb, "Failed to release allocated "
+                                       "cluster(s) in error code path.  Run "
+                                       "chkdsk to recover the lost "
+                                       "cluster(s).");
+                       NVolSetErrors(vol);
+               }
+               ntfs_free(rl2);
+               goto err_out;
+       }
+       ni->runlist.rl = rl;
+       ntfs_debug("Allocated 0x%llx clusters.", (long long)(new_alloc_size -
+                       allocated_size) >> vol->cluster_size_bits);
+       /* Find the runlist element with which the attribute extent starts. */
+       ll = sle64_to_cpu(a->data.non_resident.lowest_vcn);
+       rl2 = ntfs_rl_find_vcn_nolock(rl, ll);
+       BUG_ON(!rl2);
+       BUG_ON(!rl2->length);
+       BUG_ON(rl2->lcn < LCN_HOLE);
+       mp_rebuilt = FALSE;
+       /* Get the size for the new mapping pairs array for this extent. */
+       mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll, -1);
+       if (unlikely(mp_size <= 0)) {
+               err = mp_size;
+               if (start < 0 || start >= allocated_size)
+                       ntfs_error(vol->sb, "Cannot extend allocation of "
+                                       "inode 0x%lx, attribute type 0x%x, "
+                                       "because determining the size for the "
+                                       "mapping pairs failed with error code "
+                                       "%i.", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+               err = -EIO;
+               goto undo_alloc;
+       }
+       /* Extend the attribute record to fit the bigger mapping pairs array. */
+       attr_len = le32_to_cpu(a->length);
+       err = ntfs_attr_record_resize(m, a, mp_size +
+                       le16_to_cpu(a->data.non_resident.mapping_pairs_offset));
+       if (unlikely(err)) {
+               BUG_ON(err != -ENOSPC);
+               // TODO: Deal with this by moving this extent to a new mft
+               // record or by starting a new extent in a new mft record,
+               // possibly by extending this extent partially and filling it
+               // and creating a new extent for the remainder, or by making
+               // other attributes non-resident and/or by moving other
+               // attributes out of this mft record.
+               if (start < 0 || start >= allocated_size)
+                       ntfs_error(vol->sb, "Not enough space in the mft "
+                                       "record for the extended attribute "
+                                       "record.  This case is not "
+                                       "implemented yet.");
+               err = -EOPNOTSUPP;
+               goto undo_alloc;
+       }
+       mp_rebuilt = TRUE;
+       /* Generate the mapping pairs array directly into the attr record. */
+       err = ntfs_mapping_pairs_build(vol, (u8*)a +
+                       le16_to_cpu(a->data.non_resident.mapping_pairs_offset),
+                       mp_size, rl2, ll, -1, NULL);
+       if (unlikely(err)) {
+               if (start < 0 || start >= allocated_size)
+                       ntfs_error(vol->sb, "Cannot extend allocation of "
+                                       "inode 0x%lx, attribute type 0x%x, "
+                                       "because building the mapping pairs "
+                                       "failed with error code %i.", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+               err = -EIO;
+               goto undo_alloc;
+       }
+       /* Update the highest_vcn. */
+       a->data.non_resident.highest_vcn = cpu_to_sle64((new_alloc_size >>
+                       vol->cluster_size_bits) - 1);
+       /*
+        * We now have extended the allocated size of the attribute.  Reflect
+        * this in the ntfs_inode structure and the attribute record.
+        */
+       if (a->data.non_resident.lowest_vcn) {
+               /*
+                * We are not in the first attribute extent, switch to it, but
+                * first ensure the changes will make it to disk later.
+                */
+               flush_dcache_mft_record_page(ctx->ntfs_ino);
+               mark_mft_record_dirty(ctx->ntfs_ino);
+               ntfs_attr_reinit_search_ctx(ctx);
+               err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                               CASE_SENSITIVE, 0, NULL, 0, ctx);
+               if (unlikely(err))
+                       goto restore_undo_alloc;
+               /* @m is not used any more so no need to set it. */
+               a = ctx->attr;
+       }
+       write_lock_irqsave(&ni->size_lock, flags);
+       ni->allocated_size = new_alloc_size;
+       a->data.non_resident.allocated_size = cpu_to_sle64(new_alloc_size);
+       /*
+        * FIXME: This would fail if @ni is a directory, $MFT, or an index,
+        * since those can have sparse/compressed set.  For example can be
+        * set compressed even though it is not compressed itself and in that
+        * case the bit means that files are to be created compressed in the
+        * directory...  At present this is ok as this code is only called for
+        * regular files, and only for their $DATA attribute(s).
+        * FIXME: The calculation is wrong if we created a hole above.  For now
+        * it does not matter as we never create holes.
+        */
+       if (NInoSparse(ni) || NInoCompressed(ni)) {
+               ni->itype.compressed.size += new_alloc_size - allocated_size;
+               a->data.non_resident.compressed_size =
+                               cpu_to_sle64(ni->itype.compressed.size);
+               vi->i_blocks = ni->itype.compressed.size >> 9;
+       } else
+               vi->i_blocks = new_alloc_size >> 9;
+       write_unlock_irqrestore(&ni->size_lock, flags);
+alloc_done:
+       if (new_data_size >= 0) {
+               BUG_ON(new_data_size <
+                               sle64_to_cpu(a->data.non_resident.data_size));
+               a->data.non_resident.data_size = cpu_to_sle64(new_data_size);
+       }
+flush_done:
+       /* Ensure the changes make it to disk. */
+       flush_dcache_mft_record_page(ctx->ntfs_ino);
+       mark_mft_record_dirty(ctx->ntfs_ino);
+done:
+       ntfs_attr_put_search_ctx(ctx);
+       unmap_mft_record(base_ni);
+       up_write(&ni->runlist.lock);
+       ntfs_debug("Done, new_allocated_size 0x%llx.",
+                       (unsigned long long)new_alloc_size);
+       return new_alloc_size;
+restore_undo_alloc:
+       if (start < 0 || start >= allocated_size)
+               ntfs_error(vol->sb, "Cannot complete extension of allocation "
+                               "of inode 0x%lx, attribute type 0x%x, because "
+                               "lookup of first attribute extent failed with "
+                               "error code %i.", vi->i_ino,
+                               (unsigned)le32_to_cpu(ni->type), err);
+       if (err == -ENOENT)
+               err = -EIO;
+       ntfs_attr_reinit_search_ctx(ctx);
+       if (ntfs_attr_lookup(ni->type, ni->name, ni->name_len, CASE_SENSITIVE,
+                       allocated_size >> vol->cluster_size_bits, NULL, 0,
+                       ctx)) {
+               ntfs_error(vol->sb, "Failed to find last attribute extent of "
+                               "attribute in error code path.  Run chkdsk to "
+                               "recover.");
+               write_lock_irqsave(&ni->size_lock, flags);
+               ni->allocated_size = new_alloc_size;
+               /*
+                * FIXME: This would fail if @ni is a directory...  See above.
+                * FIXME: The calculation is wrong if we created a hole above.
+                * For now it does not matter as we never create holes.
+                */
+               if (NInoSparse(ni) || NInoCompressed(ni)) {
+                       ni->itype.compressed.size += new_alloc_size -
+                                       allocated_size;
+                       vi->i_blocks = ni->itype.compressed.size >> 9;
+               } else
+                       vi->i_blocks = new_alloc_size >> 9;
+               write_unlock_irqrestore(&ni->size_lock, flags);
+               ntfs_attr_put_search_ctx(ctx);
+               unmap_mft_record(base_ni);
+               up_write(&ni->runlist.lock);
+               /*
+                * The only thing that is now wrong is the allocated size of the
+                * base attribute extent which chkdsk should be able to fix.
+                */
+               NVolSetErrors(vol);
+               return err;
+       }
+       ctx->attr->data.non_resident.highest_vcn = cpu_to_sle64(
+                       (allocated_size >> vol->cluster_size_bits) - 1);
+undo_alloc:
+       ll = allocated_size >> vol->cluster_size_bits;
+       if (ntfs_cluster_free(ni, ll, -1, ctx) < 0) {
+               ntfs_error(vol->sb, "Failed to release allocated cluster(s) "
+                               "in error code path.  Run chkdsk to recover "
+                               "the lost cluster(s).");
+               NVolSetErrors(vol);
+       }
+       m = ctx->mrec;
+       a = ctx->attr;
+       /*
+        * If the runlist truncation fails and/or the search context is no
+        * longer valid, we cannot resize the attribute record or build the
+        * mapping pairs array thus we mark the inode bad so that no access to
+        * the freed clusters can happen.
+        */
+       if (ntfs_rl_truncate_nolock(vol, &ni->runlist, ll) || IS_ERR(m)) {
+               ntfs_error(vol->sb, "Failed to %s in error code path.  Run "
+                               "chkdsk to recover.", IS_ERR(m) ?
+                               "restore attribute search context" :
+                               "truncate attribute runlist");
+               make_bad_inode(vi);
+               make_bad_inode(VFS_I(base_ni));
+               NVolSetErrors(vol);
+       } else if (mp_rebuilt) {
+               if (ntfs_attr_record_resize(m, a, attr_len)) {
+                       ntfs_error(vol->sb, "Failed to restore attribute "
+                                       "record in error code path.  Run "
+                                       "chkdsk to recover.");
+                       make_bad_inode(vi);
+                       make_bad_inode(VFS_I(base_ni));
+                       NVolSetErrors(vol);
+               } else /* if (success) */ {
+                       if (ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu(
+                                       a->data.non_resident.
+                                       mapping_pairs_offset), attr_len -
+                                       le16_to_cpu(a->data.non_resident.
+                                       mapping_pairs_offset), rl2, ll, -1,
+                                       NULL)) {
+                               ntfs_error(vol->sb, "Failed to restore "
+                                               "mapping pairs array in error "
+                                               "code path.  Run chkdsk to "
+                                               "recover.");
+                               make_bad_inode(vi);
+                               make_bad_inode(VFS_I(base_ni));
+                               NVolSetErrors(vol);
+                       }
+                       flush_dcache_mft_record_page(ctx->ntfs_ino);
+                       mark_mft_record_dirty(ctx->ntfs_ino);
+               }
+       }
+err_out:
+       if (ctx)
+               ntfs_attr_put_search_ctx(ctx);
+       if (m)
+               unmap_mft_record(base_ni);
+       up_write(&ni->runlist.lock);
+conv_err_out:
+       ntfs_debug("Failed.  Returning error code %i.", err);
+       return err;
+}
+
 /**
  * ntfs_attr_set - fill (a part of) an attribute with a byte
  * @ni:                ntfs inode describing the attribute to fill
@@ -1773,6 +2592,8 @@ int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt, const u8 val)
                /* Finally unlock and release the page. */
                unlock_page(page);
                page_cache_release(page);
+               balance_dirty_pages_ratelimited(mapping);
+               cond_resched();
        }
        /* If there is a last partial page, need to do it the slow way. */
        if (end_ofs) {
index 0618ed6fd7b35de101192db0a058baffd99158cf..9074886b44ba194d1c6cffed6f8bb838f2263f97 100644 (file)
@@ -60,14 +60,15 @@ typedef struct {
        ATTR_RECORD *base_attr;
 } ntfs_attr_search_ctx;
 
-extern int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn);
+extern int ntfs_map_runlist_nolock(ntfs_inode *ni, VCN vcn,
+               ntfs_attr_search_ctx *ctx);
 extern int ntfs_map_runlist(ntfs_inode *ni, VCN vcn);
 
 extern LCN ntfs_attr_vcn_to_lcn_nolock(ntfs_inode *ni, const VCN vcn,
                const BOOL write_locked);
 
 extern runlist_element *ntfs_attr_find_vcn_nolock(ntfs_inode *ni,
-               const VCN vcn, const BOOL write_locked);
+               const VCN vcn, ntfs_attr_search_ctx *ctx);
 
 int ntfs_attr_lookup(const ATTR_TYPE type, const ntfschar *name,
                const u32 name_len, const IGNORE_CASE_BOOL ic,
@@ -102,7 +103,10 @@ extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);
 extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
                const u32 new_size);
 
-extern int ntfs_attr_make_non_resident(ntfs_inode *ni);
+extern int ntfs_attr_make_non_resident(ntfs_inode *ni, const u32 data_size);
+
+extern s64 ntfs_attr_extend_allocation(ntfs_inode *ni, s64 new_alloc_size,
+               const s64 new_data_size, const s64 data_start);
 
 extern int ntfs_attr_set(ntfs_inode *ni, const s64 ofs, const s64 cnt,
                const u8 val);
index be9fd1dd423d18de8bab0c320f52af5d5ba48392..7275338918132fe31eed51bddf2d48485d52c1b9 100644 (file)
  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include <linux/pagemap.h>
 #include <linux/buffer_head.h>
+#include <linux/pagemap.h>
+#include <linux/pagevec.h>
+#include <linux/sched.h>
+#include <linux/swap.h>
+#include <linux/uio.h>
+#include <linux/writeback.h>
 
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include "attrib.h"
+#include "bitmap.h"
 #include "inode.h"
 #include "debug.h"
+#include "lcnalloc.h"
+#include "malloc.h"
+#include "mft.h"
 #include "ntfs.h"
 
 /**
@@ -55,6 +68,2185 @@ static int ntfs_file_open(struct inode *vi, struct file *filp)
 
 #ifdef NTFS_RW
 
+/**
+ * ntfs_attr_extend_initialized - extend the initialized size of an attribute
+ * @ni:                        ntfs inode of the attribute to extend
+ * @new_init_size:     requested new initialized size in bytes
+ * @cached_page:       store any allocated but unused page here
+ * @lru_pvec:          lru-buffering pagevec of the caller
+ *
+ * Extend the initialized size of an attribute described by the ntfs inode @ni
+ * to @new_init_size bytes.  This involves zeroing any non-sparse space between
+ * the old initialized size and @new_init_size both in the page cache and on
+ * disk (if relevant complete pages are already uptodate in the page cache then
+ * these are simply marked dirty).
+ *
+ * As a side-effect, the file size (vfs inode->i_size) may be incremented as,
+ * in the resident attribute case, it is tied to the initialized size and, in
+ * the non-resident attribute case, it may not fall below the initialized size.
+ *
+ * Note that if the attribute is resident, we do not need to touch the page
+ * cache at all.  This is because if the page cache page is not uptodate we
+ * bring it uptodate later, when doing the write to the mft record since we
+ * then already have the page mapped.  And if the page is uptodate, the
+ * non-initialized region will already have been zeroed when the page was
+ * brought uptodate and the region may in fact already have been overwritten
+ * with new data via mmap() based writes, so we cannot just zero it.  And since
+ * POSIX specifies that the behaviour of resizing a file whilst it is mmap()ped
+ * is unspecified, we choose not to do zeroing and thus we do not need to touch
+ * the page at all.  For a more detailed explanation see ntfs_truncate() in
+ * fs/ntfs/inode.c.
+ *
+ * @cached_page and @lru_pvec are just optimizations for dealing with multiple
+ * pages.
+ *
+ * Return 0 on success and -errno on error.  In the case that an error is
+ * encountered it is possible that the initialized size will already have been
+ * incremented some way towards @new_init_size but it is guaranteed that if
+ * this is the case, the necessary zeroing will also have happened and that all
+ * metadata is self-consistent.
+ *
+ * Locking: i_sem on the vfs inode corrseponsind to the ntfs inode @ni must be
+ *         held by the caller.
+ */
+static int ntfs_attr_extend_initialized(ntfs_inode *ni, const s64 new_init_size,
+               struct page **cached_page, struct pagevec *lru_pvec)
+{
+       s64 old_init_size;
+       loff_t old_i_size;
+       pgoff_t index, end_index;
+       unsigned long flags;
+       struct inode *vi = VFS_I(ni);
+       ntfs_inode *base_ni;
+       MFT_RECORD *m = NULL;
+       ATTR_RECORD *a;
+       ntfs_attr_search_ctx *ctx = NULL;
+       struct address_space *mapping;
+       struct page *page = NULL;
+       u8 *kattr;
+       int err;
+       u32 attr_len;
+
+       read_lock_irqsave(&ni->size_lock, flags);
+       old_init_size = ni->initialized_size;
+       old_i_size = i_size_read(vi);
+       BUG_ON(new_init_size > ni->allocated_size);
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       ntfs_debug("Entering for i_ino 0x%lx, attribute type 0x%x, "
+                       "old_initialized_size 0x%llx, "
+                       "new_initialized_size 0x%llx, i_size 0x%llx.",
+                       vi->i_ino, (unsigned)le32_to_cpu(ni->type),
+                       (unsigned long long)old_init_size,
+                       (unsigned long long)new_init_size, old_i_size);
+       if (!NInoAttr(ni))
+               base_ni = ni;
+       else
+               base_ni = ni->ext.base_ntfs_ino;
+       /* Use goto to reduce indentation and we need the label below anyway. */
+       if (NInoNonResident(ni))
+               goto do_non_resident_extend;
+       BUG_ON(old_init_size != old_i_size);
+       m = map_mft_record(base_ni);
+       if (IS_ERR(m)) {
+               err = PTR_ERR(m);
+               m = NULL;
+               goto err_out;
+       }
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
+       if (unlikely(!ctx)) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                       CASE_SENSITIVE, 0, NULL, 0, ctx);
+       if (unlikely(err)) {
+               if (err == -ENOENT)
+                       err = -EIO;
+               goto err_out;
+       }
+       m = ctx->mrec;
+       a = ctx->attr;
+       BUG_ON(a->non_resident);
+       /* The total length of the attribute value. */
+       attr_len = le32_to_cpu(a->data.resident.value_length);
+       BUG_ON(old_i_size != (loff_t)attr_len);
+       /*
+        * Do the zeroing in the mft record and update the attribute size in
+        * the mft record.
+        */
+       kattr = (u8*)a + le16_to_cpu(a->data.resident.value_offset);
+       memset(kattr + attr_len, 0, new_init_size - attr_len);
+       a->data.resident.value_length = cpu_to_le32((u32)new_init_size);
+       /* Finally, update the sizes in the vfs and ntfs inodes. */
+       write_lock_irqsave(&ni->size_lock, flags);
+       i_size_write(vi, new_init_size);
+       ni->initialized_size = new_init_size;
+       write_unlock_irqrestore(&ni->size_lock, flags);
+       goto done;
+do_non_resident_extend:
+       /*
+        * If the new initialized size @new_init_size exceeds the current file
+        * size (vfs inode->i_size), we need to extend the file size to the
+        * new initialized size.
+        */
+       if (new_init_size > old_i_size) {
+               m = map_mft_record(base_ni);
+               if (IS_ERR(m)) {
+                       err = PTR_ERR(m);
+                       m = NULL;
+                       goto err_out;
+               }
+               ctx = ntfs_attr_get_search_ctx(base_ni, m);
+               if (unlikely(!ctx)) {
+                       err = -ENOMEM;
+                       goto err_out;
+               }
+               err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                               CASE_SENSITIVE, 0, NULL, 0, ctx);
+               if (unlikely(err)) {
+                       if (err == -ENOENT)
+                               err = -EIO;
+                       goto err_out;
+               }
+               m = ctx->mrec;
+               a = ctx->attr;
+               BUG_ON(!a->non_resident);
+               BUG_ON(old_i_size != (loff_t)
+                               sle64_to_cpu(a->data.non_resident.data_size));
+               a->data.non_resident.data_size = cpu_to_sle64(new_init_size);
+               flush_dcache_mft_record_page(ctx->ntfs_ino);
+               mark_mft_record_dirty(ctx->ntfs_ino);
+               /* Update the file size in the vfs inode. */
+               i_size_write(vi, new_init_size);
+               ntfs_attr_put_search_ctx(ctx);
+               ctx = NULL;
+               unmap_mft_record(base_ni);
+               m = NULL;
+       }
+       mapping = vi->i_mapping;
+       index = old_init_size >> PAGE_CACHE_SHIFT;
+       end_index = (new_init_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       do {
+               /*
+                * Read the page.  If the page is not present, this will zero
+                * the uninitialized regions for us.
+                */
+               page = read_cache_page(mapping, index,
+                               (filler_t*)mapping->a_ops->readpage, NULL);
+               if (IS_ERR(page)) {
+                       err = PTR_ERR(page);
+                       goto init_err_out;
+               }
+               wait_on_page_locked(page);
+               if (unlikely(!PageUptodate(page) || PageError(page))) {
+                       page_cache_release(page);
+                       err = -EIO;
+                       goto init_err_out;
+               }
+               /*
+                * Update the initialized size in the ntfs inode.  This is
+                * enough to make ntfs_writepage() work.
+                */
+               write_lock_irqsave(&ni->size_lock, flags);
+               ni->initialized_size = (index + 1) << PAGE_CACHE_SHIFT;
+               if (ni->initialized_size > new_init_size)
+                       ni->initialized_size = new_init_size;
+               write_unlock_irqrestore(&ni->size_lock, flags);
+               /* Set the page dirty so it gets written out. */
+               set_page_dirty(page);
+               page_cache_release(page);
+               /*
+                * Play nice with the vm and the rest of the system.  This is
+                * very much needed as we can potentially be modifying the
+                * initialised size from a very small value to a really huge
+                * value, e.g.
+                *      f = open(somefile, O_TRUNC);
+                *      truncate(f, 10GiB);
+                *      seek(f, 10GiB);
+                *      write(f, 1);
+                * And this would mean we would be marking dirty hundreds of
+                * thousands of pages or as in the above example more than
+                * two and a half million pages!
+                *
+                * TODO: For sparse pages could optimize this workload by using
+                * the FsMisc / MiscFs page bit as a "PageIsSparse" bit.  This
+                * would be set in readpage for sparse pages and here we would
+                * not need to mark dirty any pages which have this bit set.
+                * The only caveat is that we have to clear the bit everywhere
+                * where we allocate any clusters that lie in the page or that
+                * contain the page.
+                *
+                * TODO: An even greater optimization would be for us to only
+                * call readpage() on pages which are not in sparse regions as
+                * determined from the runlist.  This would greatly reduce the
+                * number of pages we read and make dirty in the case of sparse
+                * files.
+                */
+               balance_dirty_pages_ratelimited(mapping);
+               cond_resched();
+       } while (++index < end_index);
+       read_lock_irqsave(&ni->size_lock, flags);
+       BUG_ON(ni->initialized_size != new_init_size);
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       /* Now bring in sync the initialized_size in the mft record. */
+       m = map_mft_record(base_ni);
+       if (IS_ERR(m)) {
+               err = PTR_ERR(m);
+               m = NULL;
+               goto init_err_out;
+       }
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
+       if (unlikely(!ctx)) {
+               err = -ENOMEM;
+               goto init_err_out;
+       }
+       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                       CASE_SENSITIVE, 0, NULL, 0, ctx);
+       if (unlikely(err)) {
+               if (err == -ENOENT)
+                       err = -EIO;
+               goto init_err_out;
+       }
+       m = ctx->mrec;
+       a = ctx->attr;
+       BUG_ON(!a->non_resident);
+       a->data.non_resident.initialized_size = cpu_to_sle64(new_init_size);
+done:
+       flush_dcache_mft_record_page(ctx->ntfs_ino);
+       mark_mft_record_dirty(ctx->ntfs_ino);
+       if (ctx)
+               ntfs_attr_put_search_ctx(ctx);
+       if (m)
+               unmap_mft_record(base_ni);
+       ntfs_debug("Done, initialized_size 0x%llx, i_size 0x%llx.",
+                       (unsigned long long)new_init_size, i_size_read(vi));
+       return 0;
+init_err_out:
+       write_lock_irqsave(&ni->size_lock, flags);
+       ni->initialized_size = old_init_size;
+       write_unlock_irqrestore(&ni->size_lock, flags);
+err_out:
+       if (ctx)
+               ntfs_attr_put_search_ctx(ctx);
+       if (m)
+               unmap_mft_record(base_ni);
+       ntfs_debug("Failed.  Returning error code %i.", err);
+       return err;
+}
+
+/**
+ * ntfs_fault_in_pages_readable -
+ *
+ * Fault a number of userspace pages into pagetables.
+ *
+ * Unlike include/linux/pagemap.h::fault_in_pages_readable(), this one copes
+ * with more than two userspace pages as well as handling the single page case
+ * elegantly.
+ *
+ * If you find this difficult to understand, then think of the while loop being
+ * the following code, except that we do without the integer variable ret:
+ *
+ *     do {
+ *             ret = __get_user(c, uaddr);
+ *             uaddr += PAGE_SIZE;
+ *     } while (!ret && uaddr < end);
+ *
+ * Note, the final __get_user() may well run out-of-bounds of the user buffer,
+ * but _not_ out-of-bounds of the page the user buffer belongs to, and since
+ * this is only a read and not a write, and since it is still in the same page,
+ * it should not matter and this makes the code much simpler.
+ */
+static inline void ntfs_fault_in_pages_readable(const char __user *uaddr,
+               int bytes)
+{
+       const char __user *end;
+       volatile char c;
+
+       /* Set @end to the first byte outside the last page we care about. */
+       end = (const char __user*)PAGE_ALIGN((ptrdiff_t __user)uaddr + bytes);
+
+       while (!__get_user(c, uaddr) && (uaddr += PAGE_SIZE, uaddr < end))
+               ;
+}
+
+/**
+ * ntfs_fault_in_pages_readable_iovec -
+ *
+ * Same as ntfs_fault_in_pages_readable() but operates on an array of iovecs.
+ */
+static inline void ntfs_fault_in_pages_readable_iovec(const struct iovec *iov,
+               size_t iov_ofs, int bytes)
+{
+       do {
+               const char __user *buf;
+               unsigned len;
+
+               buf = iov->iov_base + iov_ofs;
+               len = iov->iov_len - iov_ofs;
+               if (len > bytes)
+                       len = bytes;
+               ntfs_fault_in_pages_readable(buf, len);
+               bytes -= len;
+               iov++;
+               iov_ofs = 0;
+       } while (bytes);
+}
+
+/**
+ * __ntfs_grab_cache_pages - obtain a number of locked pages
+ * @mapping:   address space mapping from which to obtain page cache pages
+ * @index:     starting index in @mapping at which to begin obtaining pages
+ * @nr_pages:  number of page cache pages to obtain
+ * @pages:     array of pages in which to return the obtained page cache pages
+ * @cached_page: allocated but as yet unused page
+ * @lru_pvec:  lru-buffering pagevec of caller
+ *
+ * Obtain @nr_pages locked page cache pages from the mapping @maping and
+ * starting at index @index.
+ *
+ * If a page is newly created, increment its refcount and add it to the
+ * caller's lru-buffering pagevec @lru_pvec.
+ *
+ * This is the same as mm/filemap.c::__grab_cache_page(), except that @nr_pages
+ * are obtained at once instead of just one page and that 0 is returned on
+ * success and -errno on error.
+ *
+ * Note, the page locks are obtained in ascending page index order.
+ */
+static inline int __ntfs_grab_cache_pages(struct address_space *mapping,
+               pgoff_t index, const unsigned nr_pages, struct page **pages,
+               struct page **cached_page, struct pagevec *lru_pvec)
+{
+       int err, nr;
+
+       BUG_ON(!nr_pages);
+       err = nr = 0;
+       do {
+               pages[nr] = find_lock_page(mapping, index);
+               if (!pages[nr]) {
+                       if (!*cached_page) {
+                               *cached_page = page_cache_alloc(mapping);
+                               if (unlikely(!*cached_page)) {
+                                       err = -ENOMEM;
+                                       goto err_out;
+                               }
+                       }
+                       err = add_to_page_cache(*cached_page, mapping, index,
+                                       GFP_KERNEL);
+                       if (unlikely(err)) {
+                               if (err == -EEXIST)
+                                       continue;
+                               goto err_out;
+                       }
+                       pages[nr] = *cached_page;
+                       page_cache_get(*cached_page);
+                       if (unlikely(!pagevec_add(lru_pvec, *cached_page)))
+                               __pagevec_lru_add(lru_pvec);
+                       *cached_page = NULL;
+               }
+               index++;
+               nr++;
+       } while (nr < nr_pages);
+out:
+       return err;
+err_out:
+       while (nr > 0) {
+               unlock_page(pages[--nr]);
+               page_cache_release(pages[nr]);
+       }
+       goto out;
+}
+
+static inline int ntfs_submit_bh_for_read(struct buffer_head *bh)
+{
+       lock_buffer(bh);
+       get_bh(bh);
+       bh->b_end_io = end_buffer_read_sync;
+       return submit_bh(READ, bh);
+}
+
+/**
+ * ntfs_prepare_pages_for_non_resident_write - prepare pages for receiving data
+ * @pages:     array of destination pages
+ * @nr_pages:  number of pages in @pages
+ * @pos:       byte position in file at which the write begins
+ * @bytes:     number of bytes to be written
+ *
+ * This is called for non-resident attributes from ntfs_file_buffered_write()
+ * with i_sem held on the inode (@pages[0]->mapping->host).  There are
+ * @nr_pages pages in @pages which are locked but not kmap()ped.  The source
+ * data has not yet been copied into the @pages.
+ * 
+ * Need to fill any holes with actual clusters, allocate buffers if necessary,
+ * ensure all the buffers are mapped, and bring uptodate any buffers that are
+ * only partially being written to.
+ *
+ * If @nr_pages is greater than one, we are guaranteed that the cluster size is
+ * greater than PAGE_CACHE_SIZE, that all pages in @pages are entirely inside
+ * the same cluster and that they are the entirety of that cluster, and that
+ * the cluster is sparse, i.e. we need to allocate a cluster to fill the hole.
+ *
+ * i_size is not to be modified yet.
+ *
+ * Return 0 on success or -errno on error.
+ */
+static int ntfs_prepare_pages_for_non_resident_write(struct page **pages,
+               unsigned nr_pages, s64 pos, size_t bytes)
+{
+       VCN vcn, highest_vcn = 0, cpos, cend, bh_cpos, bh_cend;
+       LCN lcn;
+       s64 bh_pos, vcn_len, end, initialized_size;
+       sector_t lcn_block;
+       struct page *page;
+       struct inode *vi;
+       ntfs_inode *ni, *base_ni = NULL;
+       ntfs_volume *vol;
+       runlist_element *rl, *rl2;
+       struct buffer_head *bh, *head, *wait[2], **wait_bh = wait;
+       ntfs_attr_search_ctx *ctx = NULL;
+       MFT_RECORD *m = NULL;
+       ATTR_RECORD *a = NULL;
+       unsigned long flags;
+       u32 attr_rec_len = 0;
+       unsigned blocksize, u;
+       int err, mp_size;
+       BOOL rl_write_locked, was_hole, is_retry;
+       unsigned char blocksize_bits;
+       struct {
+               u8 runlist_merged:1;
+               u8 mft_attr_mapped:1;
+               u8 mp_rebuilt:1;
+               u8 attr_switched:1;
+       } status = { 0, 0, 0, 0 };
+
+       BUG_ON(!nr_pages);
+       BUG_ON(!pages);
+       BUG_ON(!*pages);
+       vi = pages[0]->mapping->host;
+       ni = NTFS_I(vi);
+       vol = ni->vol;
+       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, start page "
+                       "index 0x%lx, nr_pages 0x%x, pos 0x%llx, bytes 0x%zx.",
+                       vi->i_ino, ni->type, pages[0]->index, nr_pages,
+                       (long long)pos, bytes);
+       blocksize_bits = vi->i_blkbits;
+       blocksize = 1 << blocksize_bits;
+       u = 0;
+       do {
+               struct page *page = pages[u];
+               /*
+                * create_empty_buffers() will create uptodate/dirty buffers if
+                * the page is uptodate/dirty.
+                */
+               if (!page_has_buffers(page)) {
+                       create_empty_buffers(page, blocksize, 0);
+                       if (unlikely(!page_has_buffers(page)))
+                               return -ENOMEM;
+               }
+       } while (++u < nr_pages);
+       rl_write_locked = FALSE;
+       rl = NULL;
+       err = 0;
+       vcn = lcn = -1;
+       vcn_len = 0;
+       lcn_block = -1;
+       was_hole = FALSE;
+       cpos = pos >> vol->cluster_size_bits;
+       end = pos + bytes;
+       cend = (end + vol->cluster_size - 1) >> vol->cluster_size_bits;
+       /*
+        * Loop over each page and for each page over each buffer.  Use goto to
+        * reduce indentation.
+        */
+       u = 0;
+do_next_page:
+       page = pages[u];
+       bh_pos = (s64)page->index << PAGE_CACHE_SHIFT;
+       bh = head = page_buffers(page);
+       do {
+               VCN cdelta;
+               s64 bh_end;
+               unsigned bh_cofs;
+
+               /* Clear buffer_new on all buffers to reinitialise state. */
+               if (buffer_new(bh))
+                       clear_buffer_new(bh);
+               bh_end = bh_pos + blocksize;
+               bh_cpos = bh_pos >> vol->cluster_size_bits;
+               bh_cofs = bh_pos & vol->cluster_size_mask;
+               if (buffer_mapped(bh)) {
+                       /*
+                        * The buffer is already mapped.  If it is uptodate,
+                        * ignore it.
+                        */
+                       if (buffer_uptodate(bh))
+                               continue;
+                       /*
+                        * The buffer is not uptodate.  If the page is uptodate
+                        * set the buffer uptodate and otherwise ignore it.
+                        */
+                       if (PageUptodate(page)) {
+                               set_buffer_uptodate(bh);
+                               continue;
+                       }
+                       /*
+                        * Neither the page nor the buffer are uptodate.  If
+                        * the buffer is only partially being written to, we
+                        * need to read it in before the write, i.e. now.
+                        */
+                       if ((bh_pos < pos && bh_end > pos) ||
+                                       (bh_pos < end && bh_end > end)) {
+                               /*
+                                * If the buffer is fully or partially within
+                                * the initialized size, do an actual read.
+                                * Otherwise, simply zero the buffer.
+                                */
+                               read_lock_irqsave(&ni->size_lock, flags);
+                               initialized_size = ni->initialized_size;
+                               read_unlock_irqrestore(&ni->size_lock, flags);
+                               if (bh_pos < initialized_size) {
+                                       ntfs_submit_bh_for_read(bh);
+                                       *wait_bh++ = bh;
+                               } else {
+                                       u8 *kaddr = kmap_atomic(page, KM_USER0);
+                                       memset(kaddr + bh_offset(bh), 0,
+                                                       blocksize);
+                                       kunmap_atomic(kaddr, KM_USER0);
+                                       flush_dcache_page(page);
+                                       set_buffer_uptodate(bh);
+                               }
+                       }
+                       continue;
+               }
+               /* Unmapped buffer.  Need to map it. */
+               bh->b_bdev = vol->sb->s_bdev;
+               /*
+                * If the current buffer is in the same clusters as the map
+                * cache, there is no need to check the runlist again.  The
+                * map cache is made up of @vcn, which is the first cached file
+                * cluster, @vcn_len which is the number of cached file
+                * clusters, @lcn is the device cluster corresponding to @vcn,
+                * and @lcn_block is the block number corresponding to @lcn.
+                */
+               cdelta = bh_cpos - vcn;
+               if (likely(!cdelta || (cdelta > 0 && cdelta < vcn_len))) {
+map_buffer_cached:
+                       BUG_ON(lcn < 0);
+                       bh->b_blocknr = lcn_block +
+                                       (cdelta << (vol->cluster_size_bits -
+                                       blocksize_bits)) +
+                                       (bh_cofs >> blocksize_bits);
+                       set_buffer_mapped(bh);
+                       /*
+                        * If the page is uptodate so is the buffer.  If the
+                        * buffer is fully outside the write, we ignore it if
+                        * it was already allocated and we mark it dirty so it
+                        * gets written out if we allocated it.  On the other
+                        * hand, if we allocated the buffer but we are not
+                        * marking it dirty we set buffer_new so we can do
+                        * error recovery.
+                        */
+                       if (PageUptodate(page)) {
+                               if (!buffer_uptodate(bh))
+                                       set_buffer_uptodate(bh);
+                               if (unlikely(was_hole)) {
+                                       /* We allocated the buffer. */
+                                       unmap_underlying_metadata(bh->b_bdev,
+                                                       bh->b_blocknr);
+                                       if (bh_end <= pos || bh_pos >= end)
+                                               mark_buffer_dirty(bh);
+                                       else
+                                               set_buffer_new(bh);
+                               }
+                               continue;
+                       }
+                       /* Page is _not_ uptodate. */
+                       if (likely(!was_hole)) {
+                               /*
+                                * Buffer was already allocated.  If it is not
+                                * uptodate and is only partially being written
+                                * to, we need to read it in before the write,
+                                * i.e. now.
+                                */
+                               if (!buffer_uptodate(bh) && bh_pos < end &&
+                                               bh_end > pos &&
+                                               (bh_pos < pos ||
+                                               bh_end > end)) {
+                                       /*
+                                        * If the buffer is fully or partially
+                                        * within the initialized size, do an
+                                        * actual read.  Otherwise, simply zero
+                                        * the buffer.
+                                        */
+                                       read_lock_irqsave(&ni->size_lock,
+                                                       flags);
+                                       initialized_size = ni->initialized_size;
+                                       read_unlock_irqrestore(&ni->size_lock,
+                                                       flags);
+                                       if (bh_pos < initialized_size) {
+                                               ntfs_submit_bh_for_read(bh);
+                                               *wait_bh++ = bh;
+                                       } else {
+                                               u8 *kaddr = kmap_atomic(page,
+                                                               KM_USER0);
+                                               memset(kaddr + bh_offset(bh),
+                                                               0, blocksize);
+                                               kunmap_atomic(kaddr, KM_USER0);
+                                               flush_dcache_page(page);
+                                               set_buffer_uptodate(bh);
+                                       }
+                               }
+                               continue;
+                       }
+                       /* We allocated the buffer. */
+                       unmap_underlying_metadata(bh->b_bdev, bh->b_blocknr);
+                       /*
+                        * If the buffer is fully outside the write, zero it,
+                        * set it uptodate, and mark it dirty so it gets
+                        * written out.  If it is partially being written to,
+                        * zero region surrounding the write but leave it to
+                        * commit write to do anything else.  Finally, if the
+                        * buffer is fully being overwritten, do nothing.
+                        */
+                       if (bh_end <= pos || bh_pos >= end) {
+                               if (!buffer_uptodate(bh)) {
+                                       u8 *kaddr = kmap_atomic(page, KM_USER0);
+                                       memset(kaddr + bh_offset(bh), 0,
+                                                       blocksize);
+                                       kunmap_atomic(kaddr, KM_USER0);
+                                       flush_dcache_page(page);
+                                       set_buffer_uptodate(bh);
+                               }
+                               mark_buffer_dirty(bh);
+                               continue;
+                       }
+                       set_buffer_new(bh);
+                       if (!buffer_uptodate(bh) &&
+                                       (bh_pos < pos || bh_end > end)) {
+                               u8 *kaddr;
+                               unsigned pofs;
+                                       
+                               kaddr = kmap_atomic(page, KM_USER0);
+                               if (bh_pos < pos) {
+                                       pofs = bh_pos & ~PAGE_CACHE_MASK;
+                                       memset(kaddr + pofs, 0, pos - bh_pos);
+                               }
+                               if (bh_end > end) {
+                                       pofs = end & ~PAGE_CACHE_MASK;
+                                       memset(kaddr + pofs, 0, bh_end - end);
+                               }
+                               kunmap_atomic(kaddr, KM_USER0);
+                               flush_dcache_page(page);
+                       }
+                       continue;
+               }
+               /*
+                * Slow path: this is the first buffer in the cluster.  If it
+                * is outside allocated size and is not uptodate, zero it and
+                * set it uptodate.
+                */
+               read_lock_irqsave(&ni->size_lock, flags);
+               initialized_size = ni->allocated_size;
+               read_unlock_irqrestore(&ni->size_lock, flags);
+               if (bh_pos > initialized_size) {
+                       if (PageUptodate(page)) {
+                               if (!buffer_uptodate(bh))
+                                       set_buffer_uptodate(bh);
+                       } else if (!buffer_uptodate(bh)) {
+                               u8 *kaddr = kmap_atomic(page, KM_USER0);
+                               memset(kaddr + bh_offset(bh), 0, blocksize);
+                               kunmap_atomic(kaddr, KM_USER0);
+                               flush_dcache_page(page);
+                               set_buffer_uptodate(bh);
+                       }
+                       continue;
+               }
+               is_retry = FALSE;
+               if (!rl) {
+                       down_read(&ni->runlist.lock);
+retry_remap:
+                       rl = ni->runlist.rl;
+               }
+               if (likely(rl != NULL)) {
+                       /* Seek to element containing target cluster. */
+                       while (rl->length && rl[1].vcn <= bh_cpos)
+                               rl++;
+                       lcn = ntfs_rl_vcn_to_lcn(rl, bh_cpos);
+                       if (likely(lcn >= 0)) {
+                               /*
+                                * Successful remap, setup the map cache and
+                                * use that to deal with the buffer.
+                                */
+                               was_hole = FALSE;
+                               vcn = bh_cpos;
+                               vcn_len = rl[1].vcn - vcn;
+                               lcn_block = lcn << (vol->cluster_size_bits -
+                                               blocksize_bits);
+                               cdelta = 0;
+                               /*
+                                * If the number of remaining clusters touched
+                                * by the write is smaller or equal to the
+                                * number of cached clusters, unlock the
+                                * runlist as the map cache will be used from
+                                * now on.
+                                */
+                               if (likely(vcn + vcn_len >= cend)) {
+                                       if (rl_write_locked) {
+                                               up_write(&ni->runlist.lock);
+                                               rl_write_locked = FALSE;
+                                       } else
+                                               up_read(&ni->runlist.lock);
+                                       rl = NULL;
+                               }
+                               goto map_buffer_cached;
+                       }
+               } else
+                       lcn = LCN_RL_NOT_MAPPED;
+               /*
+                * If it is not a hole and not out of bounds, the runlist is
+                * probably unmapped so try to map it now.
+                */
+               if (unlikely(lcn != LCN_HOLE && lcn != LCN_ENOENT)) {
+                       if (likely(!is_retry && lcn == LCN_RL_NOT_MAPPED)) {
+                               /* Attempt to map runlist. */
+                               if (!rl_write_locked) {
+                                       /*
+                                        * We need the runlist locked for
+                                        * writing, so if it is locked for
+                                        * reading relock it now and retry in
+                                        * case it changed whilst we dropped
+                                        * the lock.
+                                        */
+                                       up_read(&ni->runlist.lock);
+                                       down_write(&ni->runlist.lock);
+                                       rl_write_locked = TRUE;
+                                       goto retry_remap;
+                               }
+                               err = ntfs_map_runlist_nolock(ni, bh_cpos,
+                                               NULL);
+                               if (likely(!err)) {
+                                       is_retry = TRUE;
+                                       goto retry_remap;
+                               }
+                               /*
+                                * If @vcn is out of bounds, pretend @lcn is
+                                * LCN_ENOENT.  As long as the buffer is out
+                                * of bounds this will work fine.
+                                */
+                               if (err == -ENOENT) {
+                                       lcn = LCN_ENOENT;
+                                       err = 0;
+                                       goto rl_not_mapped_enoent;
+                               }
+                       } else
+                               err = -EIO;
+                       /* Failed to map the buffer, even after retrying. */
+                       bh->b_blocknr = -1;
+                       ntfs_error(vol->sb, "Failed to write to inode 0x%lx, "
+                                       "attribute type 0x%x, vcn 0x%llx, "
+                                       "vcn offset 0x%x, because its "
+                                       "location on disk could not be "
+                                       "determined%s (error code %i).",
+                                       ni->mft_no, ni->type,
+                                       (unsigned long long)bh_cpos,
+                                       (unsigned)bh_pos &
+                                       vol->cluster_size_mask,
+                                       is_retry ? " even after retrying" : "",
+                                       err);
+                       break;
+               }
+rl_not_mapped_enoent:
+               /*
+                * The buffer is in a hole or out of bounds.  We need to fill
+                * the hole, unless the buffer is in a cluster which is not
+                * touched by the write, in which case we just leave the buffer
+                * unmapped.  This can only happen when the cluster size is
+                * less than the page cache size.
+                */
+               if (unlikely(vol->cluster_size < PAGE_CACHE_SIZE)) {
+                       bh_cend = (bh_end + vol->cluster_size - 1) >>
+                                       vol->cluster_size_bits;
+                       if ((bh_cend <= cpos || bh_cpos >= cend)) {
+                               bh->b_blocknr = -1;
+                               /*
+                                * If the buffer is uptodate we skip it.  If it
+                                * is not but the page is uptodate, we can set
+                                * the buffer uptodate.  If the page is not
+                                * uptodate, we can clear the buffer and set it
+                                * uptodate.  Whether this is worthwhile is
+                                * debatable and this could be removed.
+                                */
+                               if (PageUptodate(page)) {
+                                       if (!buffer_uptodate(bh))
+                                               set_buffer_uptodate(bh);
+                               } else if (!buffer_uptodate(bh)) {
+                                       u8 *kaddr = kmap_atomic(page, KM_USER0);
+                                       memset(kaddr + bh_offset(bh), 0,
+                                                       blocksize);
+                                       kunmap_atomic(kaddr, KM_USER0);
+                                       flush_dcache_page(page);
+                                       set_buffer_uptodate(bh);
+                               }
+                               continue;
+                       }
+               }
+               /*
+                * Out of bounds buffer is invalid if it was not really out of
+                * bounds.
+                */
+               BUG_ON(lcn != LCN_HOLE);
+               /*
+                * We need the runlist locked for writing, so if it is locked
+                * for reading relock it now and retry in case it changed
+                * whilst we dropped the lock.
+                */
+               BUG_ON(!rl);
+               if (!rl_write_locked) {
+                       up_read(&ni->runlist.lock);
+                       down_write(&ni->runlist.lock);
+                       rl_write_locked = TRUE;
+                       goto retry_remap;
+               }
+               /* Find the previous last allocated cluster. */
+               BUG_ON(rl->lcn != LCN_HOLE);
+               lcn = -1;
+               rl2 = rl;
+               while (--rl2 >= ni->runlist.rl) {
+                       if (rl2->lcn >= 0) {
+                               lcn = rl2->lcn + rl2->length;
+                               break;
+                       }
+               }
+               rl2 = ntfs_cluster_alloc(vol, bh_cpos, 1, lcn, DATA_ZONE,
+                               FALSE);
+               if (IS_ERR(rl2)) {
+                       err = PTR_ERR(rl2);
+                       ntfs_debug("Failed to allocate cluster, error code %i.",
+                                       err);
+                       break;
+               }
+               lcn = rl2->lcn;
+               rl = ntfs_runlists_merge(ni->runlist.rl, rl2);
+               if (IS_ERR(rl)) {
+                       err = PTR_ERR(rl);
+                       if (err != -ENOMEM)
+                               err = -EIO;
+                       if (ntfs_cluster_free_from_rl(vol, rl2)) {
+                               ntfs_error(vol->sb, "Failed to release "
+                                               "allocated cluster in error "
+                                               "code path.  Run chkdsk to "
+                                               "recover the lost cluster.");
+                               NVolSetErrors(vol);
+                       }
+                       ntfs_free(rl2);
+                       break;
+               }
+               ni->runlist.rl = rl;
+               status.runlist_merged = 1;
+               ntfs_debug("Allocated cluster, lcn 0x%llx.", lcn);
+               /* Map and lock the mft record and get the attribute record. */
+               if (!NInoAttr(ni))
+                       base_ni = ni;
+               else
+                       base_ni = ni->ext.base_ntfs_ino;
+               m = map_mft_record(base_ni);
+               if (IS_ERR(m)) {
+                       err = PTR_ERR(m);
+                       break;
+               }
+               ctx = ntfs_attr_get_search_ctx(base_ni, m);
+               if (unlikely(!ctx)) {
+                       err = -ENOMEM;
+                       unmap_mft_record(base_ni);
+                       break;
+               }
+               status.mft_attr_mapped = 1;
+               err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                               CASE_SENSITIVE, bh_cpos, NULL, 0, ctx);
+               if (unlikely(err)) {
+                       if (err == -ENOENT)
+                               err = -EIO;
+                       break;
+               }
+               m = ctx->mrec;
+               a = ctx->attr;
+               /*
+                * Find the runlist element with which the attribute extent
+                * starts.  Note, we cannot use the _attr_ version because we
+                * have mapped the mft record.  That is ok because we know the
+                * runlist fragment must be mapped already to have ever gotten
+                * here, so we can just use the _rl_ version.
+                */
+               vcn = sle64_to_cpu(a->data.non_resident.lowest_vcn);
+               rl2 = ntfs_rl_find_vcn_nolock(rl, vcn);
+               BUG_ON(!rl2);
+               BUG_ON(!rl2->length);
+               BUG_ON(rl2->lcn < LCN_HOLE);
+               highest_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn);
+               /*
+                * If @highest_vcn is zero, calculate the real highest_vcn
+                * (which can really be zero).
+                */
+               if (!highest_vcn)
+                       highest_vcn = (sle64_to_cpu(
+                                       a->data.non_resident.allocated_size) >>
+                                       vol->cluster_size_bits) - 1;
+               /*
+                * Determine the size of the mapping pairs array for the new
+                * extent, i.e. the old extent with the hole filled.
+                */
+               mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, vcn,
+                               highest_vcn);
+               if (unlikely(mp_size <= 0)) {
+                       if (!(err = mp_size))
+                               err = -EIO;
+                       ntfs_debug("Failed to get size for mapping pairs "
+                                       "array, error code %i.", err);
+                       break;
+               }
+               /*
+                * Resize the attribute record to fit the new mapping pairs
+                * array.
+                */
+               attr_rec_len = le32_to_cpu(a->length);
+               err = ntfs_attr_record_resize(m, a, mp_size + le16_to_cpu(
+                               a->data.non_resident.mapping_pairs_offset));
+               if (unlikely(err)) {
+                       BUG_ON(err != -ENOSPC);
+                       // TODO: Deal with this by using the current attribute
+                       // and fill it with as much of the mapping pairs
+                       // array as possible.  Then loop over each attribute
+                       // extent rewriting the mapping pairs arrays as we go
+                       // along and if when we reach the end we have not
+                       // enough space, try to resize the last attribute
+                       // extent and if even that fails, add a new attribute
+                       // extent.
+                       // We could also try to resize at each step in the hope
+                       // that we will not need to rewrite every single extent.
+                       // Note, we may need to decompress some extents to fill
+                       // the runlist as we are walking the extents...
+                       ntfs_error(vol->sb, "Not enough space in the mft "
+                                       "record for the extended attribute "
+                                       "record.  This case is not "
+                                       "implemented yet.");
+                       err = -EOPNOTSUPP;
+                       break ;
+               }
+               status.mp_rebuilt = 1;
+               /*
+                * Generate the mapping pairs array directly into the attribute
+                * record.
+                */
+               err = ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu(
+                               a->data.non_resident.mapping_pairs_offset),
+                               mp_size, rl2, vcn, highest_vcn, NULL);
+               if (unlikely(err)) {
+                       ntfs_error(vol->sb, "Cannot fill hole in inode 0x%lx, "
+                                       "attribute type 0x%x, because building "
+                                       "the mapping pairs failed with error "
+                                       "code %i.", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+                       err = -EIO;
+                       break;
+               }
+               /* Update the highest_vcn but only if it was not set. */
+               if (unlikely(!a->data.non_resident.highest_vcn))
+                       a->data.non_resident.highest_vcn =
+                                       cpu_to_sle64(highest_vcn);
+               /*
+                * If the attribute is sparse/compressed, update the compressed
+                * size in the ntfs_inode structure and the attribute record.
+                */
+               if (likely(NInoSparse(ni) || NInoCompressed(ni))) {
+                       /*
+                        * If we are not in the first attribute extent, switch
+                        * to it, but first ensure the changes will make it to
+                        * disk later.
+                        */
+                       if (a->data.non_resident.lowest_vcn) {
+                               flush_dcache_mft_record_page(ctx->ntfs_ino);
+                               mark_mft_record_dirty(ctx->ntfs_ino);
+                               ntfs_attr_reinit_search_ctx(ctx);
+                               err = ntfs_attr_lookup(ni->type, ni->name,
+                                               ni->name_len, CASE_SENSITIVE,
+                                               0, NULL, 0, ctx);
+                               if (unlikely(err)) {
+                                       status.attr_switched = 1;
+                                       break;
+                               }
+                               /* @m is not used any more so do not set it. */
+                               a = ctx->attr;
+                       }
+                       write_lock_irqsave(&ni->size_lock, flags);
+                       ni->itype.compressed.size += vol->cluster_size;
+                       a->data.non_resident.compressed_size =
+                                       cpu_to_sle64(ni->itype.compressed.size);
+                       write_unlock_irqrestore(&ni->size_lock, flags);
+               }
+               /* Ensure the changes make it to disk. */
+               flush_dcache_mft_record_page(ctx->ntfs_ino);
+               mark_mft_record_dirty(ctx->ntfs_ino);
+               ntfs_attr_put_search_ctx(ctx);
+               unmap_mft_record(base_ni);
+               /* Successfully filled the hole. */
+               status.runlist_merged = 0;
+               status.mft_attr_mapped = 0;
+               status.mp_rebuilt = 0;
+               /* Setup the map cache and use that to deal with the buffer. */
+               was_hole = TRUE;
+               vcn = bh_cpos;
+               vcn_len = 1;
+               lcn_block = lcn << (vol->cluster_size_bits - blocksize_bits);
+               cdelta = 0;
+               /*
+                * If the number of remaining clusters in the @pages is smaller
+                * or equal to the number of cached clusters, unlock the
+                * runlist as the map cache will be used from now on.
+                */
+               if (likely(vcn + vcn_len >= cend)) {
+                       up_write(&ni->runlist.lock);
+                       rl_write_locked = FALSE;
+                       rl = NULL;
+               }
+               goto map_buffer_cached;
+       } while (bh_pos += blocksize, (bh = bh->b_this_page) != head);
+       /* If there are no errors, do the next page. */
+       if (likely(!err && ++u < nr_pages))
+               goto do_next_page;
+       /* If there are no errors, release the runlist lock if we took it. */
+       if (likely(!err)) {
+               if (unlikely(rl_write_locked)) {
+                       up_write(&ni->runlist.lock);
+                       rl_write_locked = FALSE;
+               } else if (unlikely(rl))
+                       up_read(&ni->runlist.lock);
+               rl = NULL;
+       }
+       /* If we issued read requests, let them complete. */
+       read_lock_irqsave(&ni->size_lock, flags);
+       initialized_size = ni->initialized_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       while (wait_bh > wait) {
+               bh = *--wait_bh;
+               wait_on_buffer(bh);
+               if (likely(buffer_uptodate(bh))) {
+                       page = bh->b_page;
+                       bh_pos = ((s64)page->index << PAGE_CACHE_SHIFT) +
+                                       bh_offset(bh);
+                       /*
+                        * If the buffer overflows the initialized size, need
+                        * to zero the overflowing region.
+                        */
+                       if (unlikely(bh_pos + blocksize > initialized_size)) {
+                               u8 *kaddr;
+                               int ofs = 0;
+
+                               if (likely(bh_pos < initialized_size))
+                                       ofs = initialized_size - bh_pos;
+                               kaddr = kmap_atomic(page, KM_USER0);
+                               memset(kaddr + bh_offset(bh) + ofs, 0,
+                                               blocksize - ofs);
+                               kunmap_atomic(kaddr, KM_USER0);
+                               flush_dcache_page(page);
+                       }
+               } else /* if (unlikely(!buffer_uptodate(bh))) */
+                       err = -EIO;
+       }
+       if (likely(!err)) {
+               /* Clear buffer_new on all buffers. */
+               u = 0;
+               do {
+                       bh = head = page_buffers(pages[u]);
+                       do {
+                               if (buffer_new(bh))
+                                       clear_buffer_new(bh);
+                       } while ((bh = bh->b_this_page) != head);
+               } while (++u < nr_pages);
+               ntfs_debug("Done.");
+               return err;
+       }
+       if (status.attr_switched) {
+               /* Get back to the attribute extent we modified. */
+               ntfs_attr_reinit_search_ctx(ctx);
+               if (ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                               CASE_SENSITIVE, bh_cpos, NULL, 0, ctx)) {
+                       ntfs_error(vol->sb, "Failed to find required "
+                                       "attribute extent of attribute in "
+                                       "error code path.  Run chkdsk to "
+                                       "recover.");
+                       write_lock_irqsave(&ni->size_lock, flags);
+                       ni->itype.compressed.size += vol->cluster_size;
+                       write_unlock_irqrestore(&ni->size_lock, flags);
+                       flush_dcache_mft_record_page(ctx->ntfs_ino);
+                       mark_mft_record_dirty(ctx->ntfs_ino);
+                       /*
+                        * The only thing that is now wrong is the compressed
+                        * size of the base attribute extent which chkdsk
+                        * should be able to fix.
+                        */
+                       NVolSetErrors(vol);
+               } else {
+                       m = ctx->mrec;
+                       a = ctx->attr;
+                       status.attr_switched = 0;
+               }
+       }
+       /*
+        * If the runlist has been modified, need to restore it by punching a
+        * hole into it and we then need to deallocate the on-disk cluster as
+        * well.  Note, we only modify the runlist if we are able to generate a
+        * new mapping pairs array, i.e. only when the mapped attribute extent
+        * is not switched.
+        */
+       if (status.runlist_merged && !status.attr_switched) {
+               BUG_ON(!rl_write_locked);
+               /* Make the file cluster we allocated sparse in the runlist. */
+               if (ntfs_rl_punch_nolock(vol, &ni->runlist, bh_cpos, 1)) {
+                       ntfs_error(vol->sb, "Failed to punch hole into "
+                                       "attribute runlist in error code "
+                                       "path.  Run chkdsk to recover the "
+                                       "lost cluster.");
+                       make_bad_inode(vi);
+                       make_bad_inode(VFS_I(base_ni));
+                       NVolSetErrors(vol);
+               } else /* if (success) */ {
+                       status.runlist_merged = 0;
+                       /*
+                        * Deallocate the on-disk cluster we allocated but only
+                        * if we succeeded in punching its vcn out of the
+                        * runlist.
+                        */
+                       down_write(&vol->lcnbmp_lock);
+                       if (ntfs_bitmap_clear_bit(vol->lcnbmp_ino, lcn)) {
+                               ntfs_error(vol->sb, "Failed to release "
+                                               "allocated cluster in error "
+                                               "code path.  Run chkdsk to "
+                                               "recover the lost cluster.");
+                               NVolSetErrors(vol);
+                       }
+                       up_write(&vol->lcnbmp_lock);
+               }
+       }
+       /*
+        * Resize the attribute record to its old size and rebuild the mapping
+        * pairs array.  Note, we only can do this if the runlist has been
+        * restored to its old state which also implies that the mapped
+        * attribute extent is not switched.
+        */
+       if (status.mp_rebuilt && !status.runlist_merged) {
+               if (ntfs_attr_record_resize(m, a, attr_rec_len)) {
+                       ntfs_error(vol->sb, "Failed to restore attribute "
+                                       "record in error code path.  Run "
+                                       "chkdsk to recover.");
+                       make_bad_inode(vi);
+                       make_bad_inode(VFS_I(base_ni));
+                       NVolSetErrors(vol);
+               } else /* if (success) */ {
+                       if (ntfs_mapping_pairs_build(vol, (u8*)a +
+                                       le16_to_cpu(a->data.non_resident.
+                                       mapping_pairs_offset), attr_rec_len -
+                                       le16_to_cpu(a->data.non_resident.
+                                       mapping_pairs_offset), ni->runlist.rl,
+                                       vcn, highest_vcn, NULL)) {
+                               ntfs_error(vol->sb, "Failed to restore "
+                                               "mapping pairs array in error "
+                                               "code path.  Run chkdsk to "
+                                               "recover.");
+                               make_bad_inode(vi);
+                               make_bad_inode(VFS_I(base_ni));
+                               NVolSetErrors(vol);
+                       }
+                       flush_dcache_mft_record_page(ctx->ntfs_ino);
+                       mark_mft_record_dirty(ctx->ntfs_ino);
+               }
+       }
+       /* Release the mft record and the attribute. */
+       if (status.mft_attr_mapped) {
+               ntfs_attr_put_search_ctx(ctx);
+               unmap_mft_record(base_ni);
+       }
+       /* Release the runlist lock. */
+       if (rl_write_locked)
+               up_write(&ni->runlist.lock);
+       else if (rl)
+               up_read(&ni->runlist.lock);
+       /*
+        * Zero out any newly allocated blocks to avoid exposing stale data.
+        * If BH_New is set, we know that the block was newly allocated above
+        * and that it has not been fully zeroed and marked dirty yet.
+        */
+       nr_pages = u;
+       u = 0;
+       end = bh_cpos << vol->cluster_size_bits;
+       do {
+               page = pages[u];
+               bh = head = page_buffers(page);
+               do {
+                       if (u == nr_pages &&
+                                       ((s64)page->index << PAGE_CACHE_SHIFT) +
+                                       bh_offset(bh) >= end)
+                               break;
+                       if (!buffer_new(bh))
+                               continue;
+                       clear_buffer_new(bh);
+                       if (!buffer_uptodate(bh)) {
+                               if (PageUptodate(page))
+                                       set_buffer_uptodate(bh);
+                               else {
+                                       u8 *kaddr = kmap_atomic(page, KM_USER0);
+                                       memset(kaddr + bh_offset(bh), 0,
+                                                       blocksize);
+                                       kunmap_atomic(kaddr, KM_USER0);
+                                       flush_dcache_page(page);
+                                       set_buffer_uptodate(bh);
+                               }
+                       }
+                       mark_buffer_dirty(bh);
+               } while ((bh = bh->b_this_page) != head);
+       } while (++u <= nr_pages);
+       ntfs_error(vol->sb, "Failed.  Returning error code %i.", err);
+       return err;
+}
+
+/*
+ * Copy as much as we can into the pages and return the number of bytes which
+ * were sucessfully copied.  If a fault is encountered then clear the pages
+ * out to (ofs + bytes) and return the number of bytes which were copied.
+ */
+static inline size_t ntfs_copy_from_user(struct page **pages,
+               unsigned nr_pages, unsigned ofs, const char __user *buf,
+               size_t bytes)
+{
+       struct page **last_page = pages + nr_pages;
+       char *kaddr;
+       size_t total = 0;
+       unsigned len;
+       int left;
+
+       do {
+               len = PAGE_CACHE_SIZE - ofs;
+               if (len > bytes)
+                       len = bytes;
+               kaddr = kmap_atomic(*pages, KM_USER0);
+               left = __copy_from_user_inatomic(kaddr + ofs, buf, len);
+               kunmap_atomic(kaddr, KM_USER0);
+               if (unlikely(left)) {
+                       /* Do it the slow way. */
+                       kaddr = kmap(*pages);
+                       left = __copy_from_user(kaddr + ofs, buf, len);
+                       kunmap(*pages);
+                       if (unlikely(left))
+                               goto err_out;
+               }
+               total += len;
+               bytes -= len;
+               if (!bytes)
+                       break;
+               buf += len;
+               ofs = 0;
+       } while (++pages < last_page);
+out:
+       return total;
+err_out:
+       total += len - left;
+       /* Zero the rest of the target like __copy_from_user(). */
+       while (++pages < last_page) {
+               bytes -= len;
+               if (!bytes)
+                       break;
+               len = PAGE_CACHE_SIZE;
+               if (len > bytes)
+                       len = bytes;
+               kaddr = kmap_atomic(*pages, KM_USER0);
+               memset(kaddr, 0, len);
+               kunmap_atomic(kaddr, KM_USER0);
+       }
+       goto out;
+}
+
+static size_t __ntfs_copy_from_user_iovec(char *vaddr,
+               const struct iovec *iov, size_t iov_ofs, size_t bytes)
+{
+       size_t total = 0;
+
+       while (1) {
+               const char __user *buf = iov->iov_base + iov_ofs;
+               unsigned len;
+               size_t left;
+
+               len = iov->iov_len - iov_ofs;
+               if (len > bytes)
+                       len = bytes;
+               left = __copy_from_user_inatomic(vaddr, buf, len);
+               total += len;
+               bytes -= len;
+               vaddr += len;
+               if (unlikely(left)) {
+                       /*
+                        * Zero the rest of the target like __copy_from_user().
+                        */
+                       memset(vaddr, 0, bytes);
+                       total -= left;
+                       break;
+               }
+               if (!bytes)
+                       break;
+               iov++;
+               iov_ofs = 0;
+       }
+       return total;
+}
+
+static inline void ntfs_set_next_iovec(const struct iovec **iovp,
+               size_t *iov_ofsp, size_t bytes)
+{
+       const struct iovec *iov = *iovp;
+       size_t iov_ofs = *iov_ofsp;
+
+       while (bytes) {
+               unsigned len;
+
+               len = iov->iov_len - iov_ofs;
+               if (len > bytes)
+                       len = bytes;
+               bytes -= len;
+               iov_ofs += len;
+               if (iov->iov_len == iov_ofs) {
+                       iov++;
+                       iov_ofs = 0;
+               }
+       }
+       *iovp = iov;
+       *iov_ofsp = iov_ofs;
+}
+
+/*
+ * This has the same side-effects and return value as ntfs_copy_from_user().
+ * The difference is that on a fault we need to memset the remainder of the
+ * pages (out to offset + bytes), to emulate ntfs_copy_from_user()'s
+ * single-segment behaviour.
+ *
+ * We call the same helper (__ntfs_copy_from_user_iovec()) both when atomic and
+ * when not atomic.  This is ok because __ntfs_copy_from_user_iovec() calls
+ * __copy_from_user_inatomic() and it is ok to call this when non-atomic.  In
+ * fact, the only difference between __copy_from_user_inatomic() and
+ * __copy_from_user() is that the latter calls might_sleep().  And on many
+ * architectures __copy_from_user_inatomic() is just defined to
+ * __copy_from_user() so it makes no difference at all on those architectures.
+ */
+static inline size_t ntfs_copy_from_user_iovec(struct page **pages,
+               unsigned nr_pages, unsigned ofs, const struct iovec **iov,
+               size_t *iov_ofs, size_t bytes)
+{
+       struct page **last_page = pages + nr_pages;
+       char *kaddr;
+       size_t copied, len, total = 0;
+
+       do {
+               len = PAGE_CACHE_SIZE - ofs;
+               if (len > bytes)
+                       len = bytes;
+               kaddr = kmap_atomic(*pages, KM_USER0);
+               copied = __ntfs_copy_from_user_iovec(kaddr + ofs,
+                               *iov, *iov_ofs, len);
+               kunmap_atomic(kaddr, KM_USER0);
+               if (unlikely(copied != len)) {
+                       /* Do it the slow way. */
+                       kaddr = kmap(*pages);
+                       copied = __ntfs_copy_from_user_iovec(kaddr + ofs,
+                                       *iov, *iov_ofs, len);
+                       kunmap(*pages);
+                       if (unlikely(copied != len))
+                               goto err_out;
+               }
+               total += len;
+               bytes -= len;
+               if (!bytes)
+                       break;
+               ntfs_set_next_iovec(iov, iov_ofs, len);
+               ofs = 0;
+       } while (++pages < last_page);
+out:
+       return total;
+err_out:
+       total += copied;
+       /* Zero the rest of the target like __copy_from_user(). */
+       while (++pages < last_page) {
+               bytes -= len;
+               if (!bytes)
+                       break;
+               len = PAGE_CACHE_SIZE;
+               if (len > bytes)
+                       len = bytes;
+               kaddr = kmap_atomic(*pages, KM_USER0);
+               memset(kaddr, 0, len);
+               kunmap_atomic(kaddr, KM_USER0);
+       }
+       goto out;
+}
+
+static inline void ntfs_flush_dcache_pages(struct page **pages,
+               unsigned nr_pages)
+{
+       BUG_ON(!nr_pages);
+       do {
+               /*
+                * Warning: Do not do the decrement at the same time as the
+                * call because flush_dcache_page() is a NULL macro on i386
+                * and hence the decrement never happens.
+                */
+               flush_dcache_page(pages[nr_pages]);
+       } while (--nr_pages > 0);
+}
+
+/**
+ * ntfs_commit_pages_after_non_resident_write - commit the received data
+ * @pages:     array of destination pages
+ * @nr_pages:  number of pages in @pages
+ * @pos:       byte position in file at which the write begins
+ * @bytes:     number of bytes to be written
+ *
+ * See description of ntfs_commit_pages_after_write(), below.
+ */
+static inline int ntfs_commit_pages_after_non_resident_write(
+               struct page **pages, const unsigned nr_pages,
+               s64 pos, size_t bytes)
+{
+       s64 end, initialized_size;
+       struct inode *vi;
+       ntfs_inode *ni, *base_ni;
+       struct buffer_head *bh, *head;
+       ntfs_attr_search_ctx *ctx;
+       MFT_RECORD *m;
+       ATTR_RECORD *a;
+       unsigned long flags;
+       unsigned blocksize, u;
+       int err;
+
+       vi = pages[0]->mapping->host;
+       ni = NTFS_I(vi);
+       blocksize = 1 << vi->i_blkbits;
+       end = pos + bytes;
+       u = 0;
+       do {
+               s64 bh_pos;
+               struct page *page;
+               BOOL partial;
+
+               page = pages[u];
+               bh_pos = (s64)page->index << PAGE_CACHE_SHIFT;
+               bh = head = page_buffers(page);
+               partial = FALSE;
+               do {
+                       s64 bh_end;
+
+                       bh_end = bh_pos + blocksize;
+                       if (bh_end <= pos || bh_pos >= end) {
+                               if (!buffer_uptodate(bh))
+                                       partial = TRUE;
+                       } else {
+                               set_buffer_uptodate(bh);
+                               mark_buffer_dirty(bh);
+                       }
+               } while (bh_pos += blocksize, (bh = bh->b_this_page) != head);
+               /*
+                * If all buffers are now uptodate but the page is not, set the
+                * page uptodate.
+                */
+               if (!partial && !PageUptodate(page))
+                       SetPageUptodate(page);
+       } while (++u < nr_pages);
+       /*
+        * Finally, if we do not need to update initialized_size or i_size we
+        * are finished.
+        */
+       read_lock_irqsave(&ni->size_lock, flags);
+       initialized_size = ni->initialized_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       if (end <= initialized_size) {
+               ntfs_debug("Done.");
+               return 0;
+       }
+       /*
+        * Update initialized_size/i_size as appropriate, both in the inode and
+        * the mft record.
+        */
+       if (!NInoAttr(ni))
+               base_ni = ni;
+       else
+               base_ni = ni->ext.base_ntfs_ino;
+       /* Map, pin, and lock the mft record. */
+       m = map_mft_record(base_ni);
+       if (IS_ERR(m)) {
+               err = PTR_ERR(m);
+               m = NULL;
+               ctx = NULL;
+               goto err_out;
+       }
+       BUG_ON(!NInoNonResident(ni));
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
+       if (unlikely(!ctx)) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                       CASE_SENSITIVE, 0, NULL, 0, ctx);
+       if (unlikely(err)) {
+               if (err == -ENOENT)
+                       err = -EIO;
+               goto err_out;
+       }
+       a = ctx->attr;
+       BUG_ON(!a->non_resident);
+       write_lock_irqsave(&ni->size_lock, flags);
+       BUG_ON(end > ni->allocated_size);
+       ni->initialized_size = end;
+       a->data.non_resident.initialized_size = cpu_to_sle64(end);
+       if (end > i_size_read(vi)) {
+               i_size_write(vi, end);
+               a->data.non_resident.data_size =
+                               a->data.non_resident.initialized_size;
+       }
+       write_unlock_irqrestore(&ni->size_lock, flags);
+       /* Mark the mft record dirty, so it gets written back. */
+       flush_dcache_mft_record_page(ctx->ntfs_ino);
+       mark_mft_record_dirty(ctx->ntfs_ino);
+       ntfs_attr_put_search_ctx(ctx);
+       unmap_mft_record(base_ni);
+       ntfs_debug("Done.");
+       return 0;
+err_out:
+       if (ctx)
+               ntfs_attr_put_search_ctx(ctx);
+       if (m)
+               unmap_mft_record(base_ni);
+       ntfs_error(vi->i_sb, "Failed to update initialized_size/i_size (error "
+                       "code %i).", err);
+       if (err != -ENOMEM) {
+               NVolSetErrors(ni->vol);
+               make_bad_inode(VFS_I(base_ni));
+               make_bad_inode(vi);
+       }
+       return err;
+}
+
+/**
+ * ntfs_commit_pages_after_write - commit the received data
+ * @pages:     array of destination pages
+ * @nr_pages:  number of pages in @pages
+ * @pos:       byte position in file at which the write begins
+ * @bytes:     number of bytes to be written
+ *
+ * This is called from ntfs_file_buffered_write() with i_sem held on the inode
+ * (@pages[0]->mapping->host).  There are @nr_pages pages in @pages which are
+ * locked but not kmap()ped.  The source data has already been copied into the
+ * @page.  ntfs_prepare_pages_for_non_resident_write() has been called before
+ * the data was copied (for non-resident attributes only) and it returned
+ * success.
+ *
+ * Need to set uptodate and mark dirty all buffers within the boundary of the
+ * write.  If all buffers in a page are uptodate we set the page uptodate, too.
+ *
+ * Setting the buffers dirty ensures that they get written out later when
+ * ntfs_writepage() is invoked by the VM.
+ *
+ * Finally, we need to update i_size and initialized_size as appropriate both
+ * in the inode and the mft record.
+ *
+ * This is modelled after fs/buffer.c::generic_commit_write(), which marks
+ * buffers uptodate and dirty, sets the page uptodate if all buffers in the
+ * page are uptodate, and updates i_size if the end of io is beyond i_size.  In
+ * that case, it also marks the inode dirty.
+ *
+ * If things have gone as outlined in
+ * ntfs_prepare_pages_for_non_resident_write(), we do not need to do any page
+ * content modifications here for non-resident attributes.  For resident
+ * attributes we need to do the uptodate bringing here which we combine with
+ * the copying into the mft record which means we save one atomic kmap.
+ *
+ * Return 0 on success or -errno on error.
+ */
+static int ntfs_commit_pages_after_write(struct page **pages,
+               const unsigned nr_pages, s64 pos, size_t bytes)
+{
+       s64 end, initialized_size;
+       loff_t i_size;
+       struct inode *vi;
+       ntfs_inode *ni, *base_ni;
+       struct page *page;
+       ntfs_attr_search_ctx *ctx;
+       MFT_RECORD *m;
+       ATTR_RECORD *a;
+       char *kattr, *kaddr;
+       unsigned long flags;
+       u32 attr_len;
+       int err;
+
+       BUG_ON(!nr_pages);
+       BUG_ON(!pages);
+       page = pages[0];
+       BUG_ON(!page);
+       vi = page->mapping->host;
+       ni = NTFS_I(vi);
+       ntfs_debug("Entering for inode 0x%lx, attribute type 0x%x, start page "
+                       "index 0x%lx, nr_pages 0x%x, pos 0x%llx, bytes 0x%zx.",
+                       vi->i_ino, ni->type, page->index, nr_pages,
+                       (long long)pos, bytes);
+       if (NInoNonResident(ni))
+               return ntfs_commit_pages_after_non_resident_write(pages,
+                               nr_pages, pos, bytes);
+       BUG_ON(nr_pages > 1);
+       /*
+        * Attribute is resident, implying it is not compressed, encrypted, or
+        * sparse.
+        */
+       if (!NInoAttr(ni))
+               base_ni = ni;
+       else
+               base_ni = ni->ext.base_ntfs_ino;
+       BUG_ON(NInoNonResident(ni));
+       /* Map, pin, and lock the mft record. */
+       m = map_mft_record(base_ni);
+       if (IS_ERR(m)) {
+               err = PTR_ERR(m);
+               m = NULL;
+               ctx = NULL;
+               goto err_out;
+       }
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
+       if (unlikely(!ctx)) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+       err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
+                       CASE_SENSITIVE, 0, NULL, 0, ctx);
+       if (unlikely(err)) {
+               if (err == -ENOENT)
+                       err = -EIO;
+               goto err_out;
+       }
+       a = ctx->attr;
+       BUG_ON(a->non_resident);
+       /* The total length of the attribute value. */
+       attr_len = le32_to_cpu(a->data.resident.value_length);
+       i_size = i_size_read(vi);
+       BUG_ON(attr_len != i_size);
+       BUG_ON(pos > attr_len);
+       end = pos + bytes;
+       BUG_ON(end > le32_to_cpu(a->length) -
+                       le16_to_cpu(a->data.resident.value_offset));
+       kattr = (u8*)a + le16_to_cpu(a->data.resident.value_offset);
+       kaddr = kmap_atomic(page, KM_USER0);
+       /* Copy the received data from the page to the mft record. */
+       memcpy(kattr + pos, kaddr + pos, bytes);
+       /* Update the attribute length if necessary. */
+       if (end > attr_len) {
+               attr_len = end;
+               a->data.resident.value_length = cpu_to_le32(attr_len);
+       }
+       /*
+        * If the page is not uptodate, bring the out of bounds area(s)
+        * uptodate by copying data from the mft record to the page.
+        */
+       if (!PageUptodate(page)) {
+               if (pos > 0)
+                       memcpy(kaddr, kattr, pos);
+               if (end < attr_len)
+                       memcpy(kaddr + end, kattr + end, attr_len - end);
+               /* Zero the region outside the end of the attribute value. */
+               memset(kaddr + attr_len, 0, PAGE_CACHE_SIZE - attr_len);
+               flush_dcache_page(page);
+               SetPageUptodate(page);
+       }
+       kunmap_atomic(kaddr, KM_USER0);
+       /* Update initialized_size/i_size if necessary. */
+       read_lock_irqsave(&ni->size_lock, flags);
+       initialized_size = ni->initialized_size;
+       BUG_ON(end > ni->allocated_size);
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       BUG_ON(initialized_size != i_size);
+       if (end > initialized_size) {
+               unsigned long flags;
+
+               write_lock_irqsave(&ni->size_lock, flags);
+               ni->initialized_size = end;
+               i_size_write(vi, end);
+               write_unlock_irqrestore(&ni->size_lock, flags);
+       }
+       /* Mark the mft record dirty, so it gets written back. */
+       flush_dcache_mft_record_page(ctx->ntfs_ino);
+       mark_mft_record_dirty(ctx->ntfs_ino);
+       ntfs_attr_put_search_ctx(ctx);
+       unmap_mft_record(base_ni);
+       ntfs_debug("Done.");
+       return 0;
+err_out:
+       if (err == -ENOMEM) {
+               ntfs_warning(vi->i_sb, "Error allocating memory required to "
+                               "commit the write.");
+               if (PageUptodate(page)) {
+                       ntfs_warning(vi->i_sb, "Page is uptodate, setting "
+                                       "dirty so the write will be retried "
+                                       "later on by the VM.");
+                       /*
+                        * Put the page on mapping->dirty_pages, but leave its
+                        * buffers' dirty state as-is.
+                        */
+                       __set_page_dirty_nobuffers(page);
+                       err = 0;
+               } else
+                       ntfs_error(vi->i_sb, "Page is not uptodate.  Written "
+                                       "data has been lost.");
+       } else {
+               ntfs_error(vi->i_sb, "Resident attribute commit write failed "
+                               "with error %i.", err);
+               NVolSetErrors(ni->vol);
+               make_bad_inode(VFS_I(base_ni));
+               make_bad_inode(vi);
+       }
+       if (ctx)
+               ntfs_attr_put_search_ctx(ctx);
+       if (m)
+               unmap_mft_record(base_ni);
+       return err;
+}
+
+/**
+ * ntfs_file_buffered_write -
+ *
+ * Locking: The vfs is holding ->i_sem on the inode.
+ */
+static ssize_t ntfs_file_buffered_write(struct kiocb *iocb,
+               const struct iovec *iov, unsigned long nr_segs,
+               loff_t pos, loff_t *ppos, size_t count)
+{
+       struct file *file = iocb->ki_filp;
+       struct address_space *mapping = file->f_mapping;
+       struct inode *vi = mapping->host;
+       ntfs_inode *ni = NTFS_I(vi);
+       ntfs_volume *vol = ni->vol;
+       struct page *pages[NTFS_MAX_PAGES_PER_CLUSTER];
+       struct page *cached_page = NULL;
+       char __user *buf = NULL;
+       s64 end, ll;
+       VCN last_vcn;
+       LCN lcn;
+       unsigned long flags;
+       size_t bytes, iov_ofs = 0;      /* Offset in the current iovec. */
+       ssize_t status, written;
+       unsigned nr_pages;
+       int err;
+       struct pagevec lru_pvec;
+
+       ntfs_debug("Entering for i_ino 0x%lx, attribute type 0x%x, "
+                       "pos 0x%llx, count 0x%lx.",
+                       vi->i_ino, (unsigned)le32_to_cpu(ni->type),
+                       (unsigned long long)pos, (unsigned long)count);
+       if (unlikely(!count))
+               return 0;
+       BUG_ON(NInoMstProtected(ni));
+       /*
+        * If the attribute is not an index root and it is encrypted or
+        * compressed, we cannot write to it yet.  Note we need to check for
+        * AT_INDEX_ALLOCATION since this is the type of both directory and
+        * index inodes.
+        */
+       if (ni->type != AT_INDEX_ALLOCATION) {
+               /* If file is encrypted, deny access, just like NT4. */
+               if (NInoEncrypted(ni)) {
+                       /*
+                        * Reminder for later: Encrypted files are _always_
+                        * non-resident so that the content can always be
+                        * encrypted.
+                        */
+                       ntfs_debug("Denying write access to encrypted file.");
+                       return -EACCES;
+               }
+               if (NInoCompressed(ni)) {
+                       /* Only unnamed $DATA attribute can be compressed. */
+                       BUG_ON(ni->type != AT_DATA);
+                       BUG_ON(ni->name_len);
+                       /*
+                        * Reminder for later: If resident, the data is not
+                        * actually compressed.  Only on the switch to non-
+                        * resident does compression kick in.  This is in
+                        * contrast to encrypted files (see above).
+                        */
+                       ntfs_error(vi->i_sb, "Writing to compressed files is "
+                                       "not implemented yet.  Sorry.");
+                       return -EOPNOTSUPP;
+               }
+       }
+       /*
+        * If a previous ntfs_truncate() failed, repeat it and abort if it
+        * fails again.
+        */
+       if (unlikely(NInoTruncateFailed(ni))) {
+               down_write(&vi->i_alloc_sem);
+               err = ntfs_truncate(vi);
+               up_write(&vi->i_alloc_sem);
+               if (err || NInoTruncateFailed(ni)) {
+                       if (!err)
+                               err = -EIO;
+                       ntfs_error(vol->sb, "Cannot perform write to inode "
+                                       "0x%lx, attribute type 0x%x, because "
+                                       "ntfs_truncate() failed (error code "
+                                       "%i).", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+                       return err;
+               }
+       }
+       /* The first byte after the write. */
+       end = pos + count;
+       /*
+        * If the write goes beyond the allocated size, extend the allocation
+        * to cover the whole of the write, rounded up to the nearest cluster.
+        */
+       read_lock_irqsave(&ni->size_lock, flags);
+       ll = ni->allocated_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       if (end > ll) {
+               /* Extend the allocation without changing the data size. */
+               ll = ntfs_attr_extend_allocation(ni, end, -1, pos);
+               if (likely(ll >= 0)) {
+                       BUG_ON(pos >= ll);
+                       /* If the extension was partial truncate the write. */
+                       if (end > ll) {
+                               ntfs_debug("Truncating write to inode 0x%lx, "
+                                               "attribute type 0x%x, because "
+                                               "the allocation was only "
+                                               "partially extended.",
+                                               vi->i_ino, (unsigned)
+                                               le32_to_cpu(ni->type));
+                               end = ll;
+                               count = ll - pos;
+                       }
+               } else {
+                       err = ll;
+                       read_lock_irqsave(&ni->size_lock, flags);
+                       ll = ni->allocated_size;
+                       read_unlock_irqrestore(&ni->size_lock, flags);
+                       /* Perform a partial write if possible or fail. */
+                       if (pos < ll) {
+                               ntfs_debug("Truncating write to inode 0x%lx, "
+                                               "attribute type 0x%x, because "
+                                               "extending the allocation "
+                                               "failed (error code %i).",
+                                               vi->i_ino, (unsigned)
+                                               le32_to_cpu(ni->type), err);
+                               end = ll;
+                               count = ll - pos;
+                       } else {
+                               ntfs_error(vol->sb, "Cannot perform write to "
+                                               "inode 0x%lx, attribute type "
+                                               "0x%x, because extending the "
+                                               "allocation failed (error "
+                                               "code %i).", vi->i_ino,
+                                               (unsigned)
+                                               le32_to_cpu(ni->type), err);
+                               return err;
+                       }
+               }
+       }
+       pagevec_init(&lru_pvec, 0);
+       written = 0;
+       /*
+        * If the write starts beyond the initialized size, extend it up to the
+        * beginning of the write and initialize all non-sparse space between
+        * the old initialized size and the new one.  This automatically also
+        * increments the vfs inode->i_size to keep it above or equal to the
+        * initialized_size.
+        */
+       read_lock_irqsave(&ni->size_lock, flags);
+       ll = ni->initialized_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       if (pos > ll) {
+               err = ntfs_attr_extend_initialized(ni, pos, &cached_page,
+                               &lru_pvec);
+               if (err < 0) {
+                       ntfs_error(vol->sb, "Cannot perform write to inode "
+                                       "0x%lx, attribute type 0x%x, because "
+                                       "extending the initialized size "
+                                       "failed (error code %i).", vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type), err);
+                       status = err;
+                       goto err_out;
+               }
+       }
+       /*
+        * Determine the number of pages per cluster for non-resident
+        * attributes.
+        */
+       nr_pages = 1;
+       if (vol->cluster_size > PAGE_CACHE_SIZE && NInoNonResident(ni))
+               nr_pages = vol->cluster_size >> PAGE_CACHE_SHIFT;
+       /* Finally, perform the actual write. */
+       last_vcn = -1;
+       if (likely(nr_segs == 1))
+               buf = iov->iov_base;
+       do {
+               VCN vcn;
+               pgoff_t idx, start_idx;
+               unsigned ofs, do_pages, u;
+               size_t copied;
+
+               start_idx = idx = pos >> PAGE_CACHE_SHIFT;
+               ofs = pos & ~PAGE_CACHE_MASK;
+               bytes = PAGE_CACHE_SIZE - ofs;
+               do_pages = 1;
+               if (nr_pages > 1) {
+                       vcn = pos >> vol->cluster_size_bits;
+                       if (vcn != last_vcn) {
+                               last_vcn = vcn;
+                               /*
+                                * Get the lcn of the vcn the write is in.  If
+                                * it is a hole, need to lock down all pages in
+                                * the cluster.
+                                */
+                               down_read(&ni->runlist.lock);
+                               lcn = ntfs_attr_vcn_to_lcn_nolock(ni, pos >>
+                                               vol->cluster_size_bits, FALSE);
+                               up_read(&ni->runlist.lock);
+                               if (unlikely(lcn < LCN_HOLE)) {
+                                       status = -EIO;
+                                       if (lcn == LCN_ENOMEM)
+                                               status = -ENOMEM;
+                                       else
+                                               ntfs_error(vol->sb, "Cannot "
+                                                       "perform write to "
+                                                       "inode 0x%lx, "
+                                                       "attribute type 0x%x, "
+                                                       "because the attribute "
+                                                       "is corrupt.",
+                                                       vi->i_ino, (unsigned)
+                                                       le32_to_cpu(ni->type));
+                                       break;
+                               }
+                               if (lcn == LCN_HOLE) {
+                                       start_idx = (pos & ~(s64)
+                                                       vol->cluster_size_mask)
+                                                       >> PAGE_CACHE_SHIFT;
+                                       bytes = vol->cluster_size - (pos &
+                                                       vol->cluster_size_mask);
+                                       do_pages = nr_pages;
+                               }
+                       }
+               }
+               if (bytes > count)
+                       bytes = count;
+               /*
+                * Bring in the user page(s) that we will copy from _first_.
+                * Otherwise there is a nasty deadlock on copying from the same
+                * page(s) as we are writing to, without it/them being marked
+                * up-to-date.  Note, at present there is nothing to stop the
+                * pages being swapped out between us bringing them into memory
+                * and doing the actual copying.
+                */
+               if (likely(nr_segs == 1))
+                       ntfs_fault_in_pages_readable(buf, bytes);
+               else
+                       ntfs_fault_in_pages_readable_iovec(iov, iov_ofs, bytes);
+               /* Get and lock @do_pages starting at index @start_idx. */
+               status = __ntfs_grab_cache_pages(mapping, start_idx, do_pages,
+                               pages, &cached_page, &lru_pvec);
+               if (unlikely(status))
+                       break;
+               /*
+                * For non-resident attributes, we need to fill any holes with
+                * actual clusters and ensure all bufferes are mapped.  We also
+                * need to bring uptodate any buffers that are only partially
+                * being written to.
+                */
+               if (NInoNonResident(ni)) {
+                       status = ntfs_prepare_pages_for_non_resident_write(
+                                       pages, do_pages, pos, bytes);
+                       if (unlikely(status)) {
+                               loff_t i_size;
+
+                               do {
+                                       unlock_page(pages[--do_pages]);
+                                       page_cache_release(pages[do_pages]);
+                               } while (do_pages);
+                               /*
+                                * The write preparation may have instantiated
+                                * allocated space outside i_size.  Trim this
+                                * off again.  We can ignore any errors in this
+                                * case as we will just be waisting a bit of
+                                * allocated space, which is not a disaster.
+                                */
+                               i_size = i_size_read(vi);
+                               if (pos + bytes > i_size)
+                                       vmtruncate(vi, i_size);
+                               break;
+                       }
+               }
+               u = (pos >> PAGE_CACHE_SHIFT) - pages[0]->index;
+               if (likely(nr_segs == 1)) {
+                       copied = ntfs_copy_from_user(pages + u, do_pages - u,
+                                       ofs, buf, bytes);
+                       buf += copied;
+               } else
+                       copied = ntfs_copy_from_user_iovec(pages + u,
+                                       do_pages - u, ofs, &iov, &iov_ofs,
+                                       bytes);
+               ntfs_flush_dcache_pages(pages + u, do_pages - u);
+               status = ntfs_commit_pages_after_write(pages, do_pages, pos,
+                               bytes);
+               if (likely(!status)) {
+                       written += copied;
+                       count -= copied;
+                       pos += copied;
+                       if (unlikely(copied != bytes))
+                               status = -EFAULT;
+               }
+               do {
+                       unlock_page(pages[--do_pages]);
+                       mark_page_accessed(pages[do_pages]);
+                       page_cache_release(pages[do_pages]);
+               } while (do_pages);
+               if (unlikely(status))
+                       break;
+               balance_dirty_pages_ratelimited(mapping);
+               cond_resched();
+       } while (count);
+err_out:
+       *ppos = pos;
+       if (cached_page)
+               page_cache_release(cached_page);
+       /* For now, when the user asks for O_SYNC, we actually give O_DSYNC. */
+       if (likely(!status)) {
+               if (unlikely((file->f_flags & O_SYNC) || IS_SYNC(vi))) {
+                       if (!mapping->a_ops->writepage || !is_sync_kiocb(iocb))
+                               status = generic_osync_inode(vi, mapping,
+                                               OSYNC_METADATA|OSYNC_DATA);
+               }
+       }
+       pagevec_lru_add(&lru_pvec);
+       ntfs_debug("Done.  Returning %s (written 0x%lx, status %li).",
+                       written ? "written" : "status", (unsigned long)written,
+                       (long)status);
+       return written ? written : status;
+}
+
+/**
+ * ntfs_file_aio_write_nolock -
+ */
+static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb,
+               const struct iovec *iov, unsigned long nr_segs, loff_t *ppos)
+{
+       struct file *file = iocb->ki_filp;
+       struct address_space *mapping = file->f_mapping;
+       struct inode *inode = mapping->host;
+       loff_t pos;
+       unsigned long seg;
+       size_t count;           /* after file limit checks */
+       ssize_t written, err;
+
+       count = 0;
+       for (seg = 0; seg < nr_segs; seg++) {
+               const struct iovec *iv = &iov[seg];
+               /*
+                * If any segment has a negative length, or the cumulative
+                * length ever wraps negative then return -EINVAL.
+                */
+               count += iv->iov_len;
+               if (unlikely((ssize_t)(count|iv->iov_len) < 0))
+                       return -EINVAL;
+               if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len))
+                       continue;
+               if (!seg)
+                       return -EFAULT;
+               nr_segs = seg;
+               count -= iv->iov_len;   /* This segment is no good */
+               break;
+       }
+       pos = *ppos;
+       vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+       /* We can write back this queue in page reclaim. */
+       current->backing_dev_info = mapping->backing_dev_info;
+       written = 0;
+       err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode));
+       if (err)
+               goto out;
+       if (!count)
+               goto out;
+       err = remove_suid(file->f_dentry);
+       if (err)
+               goto out;
+       inode_update_time(inode, 1);
+       written = ntfs_file_buffered_write(iocb, iov, nr_segs, pos, ppos,
+                       count);
+out:
+       current->backing_dev_info = NULL;
+       return written ? written : err;
+}
+
+/**
+ * ntfs_file_aio_write -
+ */
+static ssize_t ntfs_file_aio_write(struct kiocb *iocb, const char __user *buf,
+               size_t count, loff_t pos)
+{
+       struct file *file = iocb->ki_filp;
+       struct address_space *mapping = file->f_mapping;
+       struct inode *inode = mapping->host;
+       ssize_t ret;
+       struct iovec local_iov = { .iov_base = (void __user *)buf,
+                                  .iov_len = count };
+
+       BUG_ON(iocb->ki_pos != pos);
+
+       down(&inode->i_sem);
+       ret = ntfs_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos);
+       up(&inode->i_sem);
+       if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
+               int err = sync_page_range(inode, mapping, pos, ret);
+               if (err < 0)
+                       ret = err;
+       }
+       return ret;
+}
+
+/**
+ * ntfs_file_writev -
+ *
+ * Basically the same as generic_file_writev() except that it ends up calling
+ * ntfs_file_aio_write_nolock() instead of __generic_file_aio_write_nolock().
+ */
+static ssize_t ntfs_file_writev(struct file *file, const struct iovec *iov,
+               unsigned long nr_segs, loff_t *ppos)
+{
+       struct address_space *mapping = file->f_mapping;
+       struct inode *inode = mapping->host;
+       struct kiocb kiocb;
+       ssize_t ret;
+
+       down(&inode->i_sem);
+       init_sync_kiocb(&kiocb, file);
+       ret = ntfs_file_aio_write_nolock(&kiocb, iov, nr_segs, ppos);
+       if (ret == -EIOCBQUEUED)
+               ret = wait_on_sync_kiocb(&kiocb);
+       up(&inode->i_sem);
+       if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
+               int err = sync_page_range(inode, mapping, *ppos - ret, ret);
+               if (err < 0)
+                       ret = err;
+       }
+       return ret;
+}
+
+/**
+ * ntfs_file_write - simple wrapper for ntfs_file_writev()
+ */
+static ssize_t ntfs_file_write(struct file *file, const char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       struct iovec local_iov = { .iov_base = (void __user *)buf,
+                                  .iov_len = count };
+
+       return ntfs_file_writev(file, &local_iov, 1, ppos);
+}
+
 /**
  * ntfs_file_fsync - sync a file to disk
  * @filp:      file to be synced
@@ -113,39 +2305,39 @@ static int ntfs_file_fsync(struct file *filp, struct dentry *dentry,
 #endif /* NTFS_RW */
 
 struct file_operations ntfs_file_ops = {
-       .llseek         = generic_file_llseek,    /* Seek inside file. */
-       .read           = generic_file_read,      /* Read from file. */
-       .aio_read       = generic_file_aio_read,  /* Async read from file. */
-       .readv          = generic_file_readv,     /* Read from file. */
+       .llseek         = generic_file_llseek,   /* Seek inside file. */
+       .read           = generic_file_read,     /* Read from file. */
+       .aio_read       = generic_file_aio_read, /* Async read from file. */
+       .readv          = generic_file_readv,    /* Read from file. */
 #ifdef NTFS_RW
-       .write          = generic_file_write,     /* Write to file. */
-       .aio_write      = generic_file_aio_write, /* Async write to file. */
-       .writev         = generic_file_writev,    /* Write to file. */
-       /*.release      = ,*/                     /* Last file is closed.  See
-                                                    fs/ext2/file.c::
-                                                    ext2_release_file() for
-                                                    how to use this to discard
-                                                    preallocated space for
-                                                    write opened files. */
-       .fsync          = ntfs_file_fsync,        /* Sync a file to disk. */
-       /*.aio_fsync    = ,*/                     /* Sync all outstanding async
-                                                    i/o operations on a
-                                                    kiocb. */
+       .write          = ntfs_file_write,       /* Write to file. */
+       .aio_write      = ntfs_file_aio_write,   /* Async write to file. */
+       .writev         = ntfs_file_writev,      /* Write to file. */
+       /*.release      = ,*/                    /* Last file is closed.  See
+                                                   fs/ext2/file.c::
+                                                   ext2_release_file() for
+                                                   how to use this to discard
+                                                   preallocated space for
+                                                   write opened files. */
+       .fsync          = ntfs_file_fsync,       /* Sync a file to disk. */
+       /*.aio_fsync    = ,*/                    /* Sync all outstanding async
+                                                   i/o operations on a
+                                                   kiocb. */
 #endif /* NTFS_RW */
-       /*.ioctl        = ,*/                     /* Perform function on the
-                                                    mounted filesystem. */
-       .mmap           = generic_file_mmap,      /* Mmap file. */
-       .open           = ntfs_file_open,         /* Open file. */
-       .sendfile       = generic_file_sendfile,  /* Zero-copy data send with
-                                                    the data source being on
-                                                    the ntfs partition.  We
-                                                    do not need to care about
-                                                    the data destination. */
-       /*.sendpage     = ,*/                     /* Zero-copy data send with
-                                                    the data destination being
-                                                    on the ntfs partition.  We
-                                                    do not need to care about
-                                                    the data source. */
+       /*.ioctl        = ,*/                    /* Perform function on the
+                                                   mounted filesystem. */
+       .mmap           = generic_file_mmap,     /* Mmap file. */
+       .open           = ntfs_file_open,        /* Open file. */
+       .sendfile       = generic_file_sendfile, /* Zero-copy data send with
+                                                   the data source being on
+                                                   the ntfs partition.  We do
+                                                   not need to care about the
+                                                   data destination. */
+       /*.sendpage     = ,*/                    /* Zero-copy data send with
+                                                   the data destination being
+                                                   on the ntfs partition.  We
+                                                   do not need to care about
+                                                   the data source. */
 };
 
 struct inode_operations ntfs_file_inode_ops = {
index 7ec045131808b14546333a16009746729a2d236d..b24f4c4b2c5ccff9e80a8018ceb56b296b7ae74c 100644 (file)
@@ -30,6 +30,7 @@
 #include "debug.h"
 #include "inode.h"
 #include "attrib.h"
+#include "lcnalloc.h"
 #include "malloc.h"
 #include "mft.h"
 #include "time.h"
@@ -2291,11 +2292,16 @@ int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt)
 
 #ifdef NTFS_RW
 
+static const char *es = "  Leaving inconsistent metadata.  Unmount and run "
+               "chkdsk.";
+
 /**
  * ntfs_truncate - called when the i_size of an ntfs inode is changed
  * @vi:                inode for which the i_size was changed
  *
- * We do not support i_size changes yet.
+ * We only support i_size changes for normal files at present, i.e. not
+ * compressed and not encrypted.  This is enforced in ntfs_setattr(), see
+ * below.
  *
  * The kernel guarantees that @vi is a regular file (S_ISREG() is true) and
  * that the change is allowed.
@@ -2306,80 +2312,499 @@ int ntfs_show_options(struct seq_file *sf, struct vfsmount *mnt)
  * Returns 0 on success or -errno on error.
  *
  * Called with ->i_sem held.  In all but one case ->i_alloc_sem is held for
- * writing.  The only case where ->i_alloc_sem is not held is
+ * writing.  The only case in the kernel where ->i_alloc_sem is not held is
  * mm/filemap.c::generic_file_buffered_write() where vmtruncate() is called
- * with the current i_size as the offset which means that it is a noop as far
- * as ntfs_truncate() is concerned.
+ * with the current i_size as the offset.  The analogous place in NTFS is in
+ * fs/ntfs/file.c::ntfs_file_buffered_write() where we call vmtruncate() again
+ * without holding ->i_alloc_sem.
  */
 int ntfs_truncate(struct inode *vi)
 {
-       ntfs_inode *ni = NTFS_I(vi);
+       s64 new_size, old_size, nr_freed, new_alloc_size, old_alloc_size;
+       VCN highest_vcn;
+       unsigned long flags;
+       ntfs_inode *base_ni, *ni = NTFS_I(vi);
        ntfs_volume *vol = ni->vol;
        ntfs_attr_search_ctx *ctx;
        MFT_RECORD *m;
        ATTR_RECORD *a;
        const char *te = "  Leaving file length out of sync with i_size.";
-       int err;
+       int err, mp_size, size_change, alloc_change;
+       u32 attr_len;
 
        ntfs_debug("Entering for inode 0x%lx.", vi->i_ino);
        BUG_ON(NInoAttr(ni));
+       BUG_ON(S_ISDIR(vi->i_mode));
+       BUG_ON(NInoMstProtected(ni));
        BUG_ON(ni->nr_extents < 0);
-       m = map_mft_record(ni);
+retry_truncate:
+       /*
+        * Lock the runlist for writing and map the mft record to ensure it is
+        * safe to mess with the attribute runlist and sizes.
+        */
+       down_write(&ni->runlist.lock);
+       if (!NInoAttr(ni))
+               base_ni = ni;
+       else
+               base_ni = ni->ext.base_ntfs_ino;
+       m = map_mft_record(base_ni);
        if (IS_ERR(m)) {
                err = PTR_ERR(m);
                ntfs_error(vi->i_sb, "Failed to map mft record for inode 0x%lx "
                                "(error code %d).%s", vi->i_ino, err, te);
                ctx = NULL;
                m = NULL;
-               goto err_out;
+               goto old_bad_out;
        }
-       ctx = ntfs_attr_get_search_ctx(ni, m);
+       ctx = ntfs_attr_get_search_ctx(base_ni, m);
        if (unlikely(!ctx)) {
                ntfs_error(vi->i_sb, "Failed to allocate a search context for "
                                "inode 0x%lx (not enough memory).%s",
                                vi->i_ino, te);
                err = -ENOMEM;
-               goto err_out;
+               goto old_bad_out;
        }
        err = ntfs_attr_lookup(ni->type, ni->name, ni->name_len,
                        CASE_SENSITIVE, 0, NULL, 0, ctx);
        if (unlikely(err)) {
-               if (err == -ENOENT)
+               if (err == -ENOENT) {
                        ntfs_error(vi->i_sb, "Open attribute is missing from "
                                        "mft record.  Inode 0x%lx is corrupt.  "
-                                       "Run chkdsk.", vi->i_ino);
-               else
+                                       "Run chkdsk.%s", vi->i_ino, te);
+                       err = -EIO;
+               } else
                        ntfs_error(vi->i_sb, "Failed to lookup attribute in "
-                                       "inode 0x%lx (error code %d).",
-                                       vi->i_ino, err);
-               goto err_out;
+                                       "inode 0x%lx (error code %d).%s",
+                                       vi->i_ino, err, te);
+               goto old_bad_out;
        }
+       m = ctx->mrec;
        a = ctx->attr;
-       /* If the size has not changed there is nothing to do. */
-       if (ntfs_attr_size(a) == i_size_read(vi))
-               goto done;
-       // TODO: Implement the truncate...
-       ntfs_error(vi->i_sb, "Inode size has changed but this is not "
-                       "implemented yet.  Resetting inode size to old value. "
-                       " This is most likely a bug in the ntfs driver!");
-       i_size_write(vi, ntfs_attr_size(a)); 
-done:
+       /*
+        * The i_size of the vfs inode is the new size for the attribute value.
+        */
+       new_size = i_size_read(vi);
+       /* The current size of the attribute value is the old size. */
+       old_size = ntfs_attr_size(a);
+       /* Calculate the new allocated size. */
+       if (NInoNonResident(ni))
+               new_alloc_size = (new_size + vol->cluster_size - 1) &
+                               ~(s64)vol->cluster_size_mask;
+       else
+               new_alloc_size = (new_size + 7) & ~7;
+       /* The current allocated size is the old allocated size. */
+       read_lock_irqsave(&ni->size_lock, flags);
+       old_alloc_size = ni->allocated_size;
+       read_unlock_irqrestore(&ni->size_lock, flags);
+       /*
+        * The change in the file size.  This will be 0 if no change, >0 if the
+        * size is growing, and <0 if the size is shrinking.
+        */
+       size_change = -1;
+       if (new_size - old_size >= 0) {
+               size_change = 1;
+               if (new_size == old_size)
+                       size_change = 0;
+       }
+       /* As above for the allocated size. */
+       alloc_change = -1;
+       if (new_alloc_size - old_alloc_size >= 0) {
+               alloc_change = 1;
+               if (new_alloc_size == old_alloc_size)
+                       alloc_change = 0;
+       }
+       /*
+        * If neither the size nor the allocation are being changed there is
+        * nothing to do.
+        */
+       if (!size_change && !alloc_change)
+               goto unm_done;
+       /* If the size is changing, check if new size is allowed in $AttrDef. */
+       if (size_change) {
+               err = ntfs_attr_size_bounds_check(vol, ni->type, new_size);
+               if (unlikely(err)) {
+                       if (err == -ERANGE) {
+                               ntfs_error(vol->sb, "Truncate would cause the "
+                                               "inode 0x%lx to %simum size "
+                                               "for its attribute type "
+                                               "(0x%x).  Aborting truncate.",
+                                               vi->i_ino,
+                                               new_size > old_size ? "exceed "
+                                               "the max" : "go under the min",
+                                               le32_to_cpu(ni->type));
+                               err = -EFBIG;
+                       } else {
+                               ntfs_error(vol->sb, "Inode 0x%lx has unknown "
+                                               "attribute type 0x%x.  "
+                                               "Aborting truncate.",
+                                               vi->i_ino,
+                                               le32_to_cpu(ni->type));
+                               err = -EIO;
+                       }
+                       /* Reset the vfs inode size to the old size. */
+                       i_size_write(vi, old_size);
+                       goto err_out;
+               }
+       }
+       if (NInoCompressed(ni) || NInoEncrypted(ni)) {
+               ntfs_warning(vi->i_sb, "Changes in inode size are not "
+                               "supported yet for %s files, ignoring.",
+                               NInoCompressed(ni) ? "compressed" :
+                               "encrypted");
+               err = -EOPNOTSUPP;
+               goto bad_out;
+       }
+       if (a->non_resident)
+               goto do_non_resident_truncate;
+       BUG_ON(NInoNonResident(ni));
+       /* Resize the attribute record to best fit the new attribute size. */
+       if (new_size < vol->mft_record_size &&
+                       !ntfs_resident_attr_value_resize(m, a, new_size)) {
+               unsigned long flags;
+
+               /* The resize succeeded! */
+               flush_dcache_mft_record_page(ctx->ntfs_ino);
+               mark_mft_record_dirty(ctx->ntfs_ino);
+               write_lock_irqsave(&ni->size_lock, flags);
+               /* Update the sizes in the ntfs inode and all is done. */
+               ni->allocated_size = le32_to_cpu(a->length) -
+                               le16_to_cpu(a->data.resident.value_offset);
+               /*
+                * Note ntfs_resident_attr_value_resize() has already done any
+                * necessary data clearing in the attribute record.  When the
+                * file is being shrunk vmtruncate() will already have cleared
+                * the top part of the last partial page, i.e. since this is
+                * the resident case this is the page with index 0.  However,
+                * when the file is being expanded, the page cache page data
+                * between the old data_size, i.e. old_size, and the new_size
+                * has not been zeroed.  Fortunately, we do not need to zero it
+                * either since on one hand it will either already be zero due
+                * to both readpage and writepage clearing partial page data
+                * beyond i_size in which case there is nothing to do or in the
+                * case of the file being mmap()ped at the same time, POSIX
+                * specifies that the behaviour is unspecified thus we do not
+                * have to do anything.  This means that in our implementation
+                * in the rare case that the file is mmap()ped and a write
+                * occured into the mmap()ped region just beyond the file size
+                * and writepage has not yet been called to write out the page
+                * (which would clear the area beyond the file size) and we now
+                * extend the file size to incorporate this dirty region
+                * outside the file size, a write of the page would result in
+                * this data being written to disk instead of being cleared.
+                * Given both POSIX and the Linux mmap(2) man page specify that
+                * this corner case is undefined, we choose to leave it like
+                * that as this is much simpler for us as we cannot lock the
+                * relevant page now since we are holding too many ntfs locks
+                * which would result in a lock reversal deadlock.
+                */
+               ni->initialized_size = new_size;
+               write_unlock_irqrestore(&ni->size_lock, flags);
+               goto unm_done;
+       }
+       /* If the above resize failed, this must be an attribute extension. */
+       BUG_ON(size_change < 0);
+       /*
+        * We have to drop all the locks so we can call
+        * ntfs_attr_make_non_resident().  This could be optimised by try-
+        * locking the first page cache page and only if that fails dropping
+        * the locks, locking the page, and redoing all the locking and
+        * lookups.  While this would be a huge optimisation, it is not worth
+        * it as this is definitely a slow code path as it only ever can happen
+        * once for any given file.
+        */
        ntfs_attr_put_search_ctx(ctx);
-       unmap_mft_record(ni);
-       NInoClearTruncateFailed(ni);
-       ntfs_debug("Done.");
-       return 0;
-err_out:
-       if (err != -ENOMEM) {
+       unmap_mft_record(base_ni);
+       up_write(&ni->runlist.lock);
+       /*
+        * Not enough space in the mft record, try to make the attribute
+        * non-resident and if successful restart the truncation process.
+        */
+       err = ntfs_attr_make_non_resident(ni, old_size);
+       if (likely(!err))
+               goto retry_truncate;
+       /*
+        * Could not make non-resident.  If this is due to this not being
+        * permitted for this attribute type or there not being enough space,
+        * try to make other attributes non-resident.  Otherwise fail.
+        */
+       if (unlikely(err != -EPERM && err != -ENOSPC)) {
+               ntfs_error(vol->sb, "Cannot truncate inode 0x%lx, attribute "
+                               "type 0x%x, because the conversion from "
+                               "resident to non-resident attribute failed "
+                               "with error code %i.", vi->i_ino,
+                               (unsigned)le32_to_cpu(ni->type), err);
+               if (err != -ENOMEM)
+                       err = -EIO;
+               goto conv_err_out;
+       }
+       /* TODO: Not implemented from here, abort. */
+       if (err == -ENOSPC)
+               ntfs_error(vol->sb, "Not enough space in the mft record/on "
+                               "disk for the non-resident attribute value.  "
+                               "This case is not implemented yet.");
+       else /* if (err == -EPERM) */
+               ntfs_error(vol->sb, "This attribute type may not be "
+                               "non-resident.  This case is not implemented "
+                               "yet.");
+       err = -EOPNOTSUPP;
+       goto conv_err_out;
+#if 0
+       // TODO: Attempt to make other attributes non-resident.
+       if (!err)
+               goto do_resident_extend;
+       /*
+        * Both the attribute list attribute and the standard information
+        * attribute must remain in the base inode.  Thus, if this is one of
+        * these attributes, we have to try to move other attributes out into
+        * extent mft records instead.
+        */
+       if (ni->type == AT_ATTRIBUTE_LIST ||
+                       ni->type == AT_STANDARD_INFORMATION) {
+               // TODO: Attempt to move other attributes into extent mft
+               // records.
+               err = -EOPNOTSUPP;
+               if (!err)
+                       goto do_resident_extend;
+               goto err_out;
+       }
+       // TODO: Attempt to move this attribute to an extent mft record, but
+       // only if it is not already the only attribute in an mft record in
+       // which case there would be nothing to gain.
+       err = -EOPNOTSUPP;
+       if (!err)
+               goto do_resident_extend;
+       /* There is nothing we can do to make enough space. )-: */
+       goto err_out;
+#endif
+do_non_resident_truncate:
+       BUG_ON(!NInoNonResident(ni));
+       if (alloc_change < 0) {
+               highest_vcn = sle64_to_cpu(a->data.non_resident.highest_vcn);
+               if (highest_vcn > 0 &&
+                               old_alloc_size >> vol->cluster_size_bits >
+                               highest_vcn + 1) {
+                       /*
+                        * This attribute has multiple extents.  Not yet
+                        * supported.
+                        */
+                       ntfs_error(vol->sb, "Cannot truncate inode 0x%lx, "
+                                       "attribute type 0x%x, because the "
+                                       "attribute is highly fragmented (it "
+                                       "consists of multiple extents) and "
+                                       "this case is not implemented yet.",
+                                       vi->i_ino,
+                                       (unsigned)le32_to_cpu(ni->type));
+                       err = -EOPNOTSUPP;
+                       goto bad_out;
+               }
+       }
+       /*
+        * If the size is shrinking, need to reduce the initialized_size and
+        * the data_size before reducing the allocation.
+        */
+       if (size_change < 0) {
+               /*
+                * Make the valid size smaller (i_size is already up-to-date).
+                */
+               write_lock_irqsave(&ni->size_lock, flags);
+               if (new_size < ni->initialized_size) {
+                       ni->initialized_size = new_size;
+                       a->data.non_resident.initialized_size =
+                                       cpu_to_sle64(new_size);
+               }
+               a->data.non_resident.data_size = cpu_to_sle64(new_size);
+               write_unlock_irqrestore(&ni->size_lock, flags);
+               flush_dcache_mft_record_page(ctx->ntfs_ino);
+               mark_mft_record_dirty(ctx->ntfs_ino);
+               /* If the allocated size is not changing, we are done. */
+               if (!alloc_change)
+                       goto unm_done;
+               /*
+                * If the size is shrinking it makes no sense for the
+                * allocation to be growing.
+                */
+               BUG_ON(alloc_change > 0);
+       } else /* if (size_change >= 0) */ {
+               /*
+                * The file size is growing or staying the same but the
+                * allocation can be shrinking, growing or staying the same.
+                */
+               if (alloc_change > 0) {
+                       /*
+                        * We need to extend the allocation and possibly update
+                        * the data size.  If we are updating the data size,
+                        * since we are not touching the initialized_size we do
+                        * not need to worry about the actual data on disk.
+                        * And as far as the page cache is concerned, there
+                        * will be no pages beyond the old data size and any
+                        * partial region in the last page between the old and
+                        * new data size (or the end of the page if the new
+                        * data size is outside the page) does not need to be
+                        * modified as explained above for the resident
+                        * attribute truncate case.  To do this, we simply drop
+                        * the locks we hold and leave all the work to our
+                        * friendly helper ntfs_attr_extend_allocation().
+                        */
+                       ntfs_attr_put_search_ctx(ctx);
+                       unmap_mft_record(base_ni);
+                       up_write(&ni->runlist.lock);
+                       err = ntfs_attr_extend_allocation(ni, new_size,
+                                       size_change > 0 ? new_size : -1, -1);
+                       /*
+                        * ntfs_attr_extend_allocation() will have done error
+                        * output already.
+                        */
+                       goto done;
+               }
+               if (!alloc_change)
+                       goto alloc_done;
+       }
+       /* alloc_change < 0 */
+       /* Free the clusters. */
+       nr_freed = ntfs_cluster_free(ni, new_alloc_size >>
+                       vol->cluster_size_bits, -1, ctx);
+       m = ctx->mrec;
+       a = ctx->attr;
+       if (unlikely(nr_freed < 0)) {
+               ntfs_error(vol->sb, "Failed to release cluster(s) (error code "
+                               "%lli).  Unmount and run chkdsk to recover "
+                               "the lost cluster(s).", (long long)nr_freed);
                NVolSetErrors(vol);
+               nr_freed = 0;
+       }
+       /* Truncate the runlist. */
+       err = ntfs_rl_truncate_nolock(vol, &ni->runlist,
+                       new_alloc_size >> vol->cluster_size_bits);
+       /*
+        * If the runlist truncation failed and/or the search context is no
+        * longer valid, we cannot resize the attribute record or build the
+        * mapping pairs array thus we mark the inode bad so that no access to
+        * the freed clusters can happen.
+        */
+       if (unlikely(err || IS_ERR(m))) {
+               ntfs_error(vol->sb, "Failed to %s (error code %li).%s",
+                               IS_ERR(m) ?
+                               "restore attribute search context" :
+                               "truncate attribute runlist",
+                               IS_ERR(m) ? PTR_ERR(m) : err, es);
+               err = -EIO;
+               goto bad_out;
+       }
+       /* Get the size for the shrunk mapping pairs array for the runlist. */
+       mp_size = ntfs_get_size_for_mapping_pairs(vol, ni->runlist.rl, 0, -1);
+       if (unlikely(mp_size <= 0)) {
+               ntfs_error(vol->sb, "Cannot shrink allocation of inode 0x%lx, "
+                               "attribute type 0x%x, because determining the "
+                               "size for the mapping pairs failed with error "
+                               "code %i.%s", vi->i_ino,
+                               (unsigned)le32_to_cpu(ni->type), mp_size, es);
+               err = -EIO;
+               goto bad_out;
+       }
+       /*
+        * Shrink the attribute record for the new mapping pairs array.  Note,
+        * this cannot fail since we are making the attribute smaller thus by
+        * definition there is enough space to do so.
+        */
+       attr_len = le32_to_cpu(a->length);
+       err = ntfs_attr_record_resize(m, a, mp_size +
+                       le16_to_cpu(a->data.non_resident.mapping_pairs_offset));
+       BUG_ON(err);
+       /*
+        * Generate the mapping pairs array directly into the attribute record.
+        */
+       err = ntfs_mapping_pairs_build(vol, (u8*)a +
+                       le16_to_cpu(a->data.non_resident.mapping_pairs_offset),
+                       mp_size, ni->runlist.rl, 0, -1, NULL);
+       if (unlikely(err)) {
+               ntfs_error(vol->sb, "Cannot shrink allocation of inode 0x%lx, "
+                               "attribute type 0x%x, because building the "
+                               "mapping pairs failed with error code %i.%s",
+                               vi->i_ino, (unsigned)le32_to_cpu(ni->type),
+                               err, es);
+               err = -EIO;
+               goto bad_out;
+       }
+       /* Update the allocated/compressed size as well as the highest vcn. */
+       a->data.non_resident.highest_vcn = cpu_to_sle64((new_alloc_size >>
+                       vol->cluster_size_bits) - 1);
+       write_lock_irqsave(&ni->size_lock, flags);
+       ni->allocated_size = new_alloc_size;
+       a->data.non_resident.allocated_size = cpu_to_sle64(new_alloc_size);
+       if (NInoSparse(ni) || NInoCompressed(ni)) {
+               if (nr_freed) {
+                       ni->itype.compressed.size -= nr_freed <<
+                                       vol->cluster_size_bits;
+                       BUG_ON(ni->itype.compressed.size < 0);
+                       a->data.non_resident.compressed_size = cpu_to_sle64(
+                                       ni->itype.compressed.size);
+                       vi->i_blocks = ni->itype.compressed.size >> 9;
+               }
+       } else
+               vi->i_blocks = new_alloc_size >> 9;
+       write_unlock_irqrestore(&ni->size_lock, flags);
+       /*
+        * We have shrunk the allocation.  If this is a shrinking truncate we
+        * have already dealt with the initialized_size and the data_size above
+        * and we are done.  If the truncate is only changing the allocation
+        * and not the data_size, we are also done.  If this is an extending
+        * truncate, need to extend the data_size now which is ensured by the
+        * fact that @size_change is positive.
+        */
+alloc_done:
+       /*
+        * If the size is growing, need to update it now.  If it is shrinking,
+        * we have already updated it above (before the allocation change).
+        */
+       if (size_change > 0)
+               a->data.non_resident.data_size = cpu_to_sle64(new_size);
+       /* Ensure the modified mft record is written out. */
+       flush_dcache_mft_record_page(ctx->ntfs_ino);
+       mark_mft_record_dirty(ctx->ntfs_ino);
+unm_done:
+       ntfs_attr_put_search_ctx(ctx);
+       unmap_mft_record(base_ni);
+       up_write(&ni->runlist.lock);
+done:
+       /* Update the mtime and ctime on the base inode. */
+       inode_update_time(VFS_I(base_ni), 1);
+       if (likely(!err)) {
+               NInoClearTruncateFailed(ni);
+               ntfs_debug("Done.");
+       }
+       return err;
+old_bad_out:
+       old_size = -1;
+bad_out:
+       if (err != -ENOMEM && err != -EOPNOTSUPP) {
                make_bad_inode(vi);
+               make_bad_inode(VFS_I(base_ni));
+               NVolSetErrors(vol);
        }
+       if (err != -EOPNOTSUPP)
+               NInoSetTruncateFailed(ni);
+       else if (old_size >= 0)
+               i_size_write(vi, old_size);
+err_out:
        if (ctx)
                ntfs_attr_put_search_ctx(ctx);
        if (m)
-               unmap_mft_record(ni);
-       NInoSetTruncateFailed(ni);
+               unmap_mft_record(base_ni);
+       up_write(&ni->runlist.lock);
+out:
+       ntfs_debug("Failed.  Returning error code %i.", err);
        return err;
+conv_err_out:
+       if (err != -ENOMEM && err != -EOPNOTSUPP) {
+               make_bad_inode(vi);
+               make_bad_inode(VFS_I(base_ni));
+               NVolSetErrors(vol);
+       }
+       if (err != -EOPNOTSUPP)
+               NInoSetTruncateFailed(ni);
+       else
+               i_size_write(vi, old_size);
+       goto out;
 }
 
 /**
@@ -2420,8 +2845,7 @@ int ntfs_setattr(struct dentry *dentry, struct iattr *attr)
 
        err = inode_change_ok(vi, attr);
        if (err)
-               return err;
-
+               goto out;
        /* We do not support NTFS ACLs yet. */
        if (ia_valid & (ATTR_UID | ATTR_GID | ATTR_MODE)) {
                ntfs_warning(vi->i_sb, "Changes in user/group/mode are not "
@@ -2429,14 +2853,22 @@ int ntfs_setattr(struct dentry *dentry, struct iattr *attr)
                err = -EOPNOTSUPP;
                goto out;
        }
-
        if (ia_valid & ATTR_SIZE) {
                if (attr->ia_size != i_size_read(vi)) {
-                       ntfs_warning(vi->i_sb, "Changes in inode size are not "
-                                       "supported yet, ignoring.");
-                       err = -EOPNOTSUPP;
-                       // TODO: Implement...
-                       // err = vmtruncate(vi, attr->ia_size);
+                       ntfs_inode *ni = NTFS_I(vi);
+                       /*
+                        * FIXME: For now we do not support resizing of
+                        * compressed or encrypted files yet.
+                        */
+                       if (NInoCompressed(ni) || NInoEncrypted(ni)) {
+                               ntfs_warning(vi->i_sb, "Changes in inode size "
+                                               "are not supported yet for "
+                                               "%s files, ignoring.",
+                                               NInoCompressed(ni) ?
+                                               "compressed" : "encrypted");
+                               err = -EOPNOTSUPP;
+                       } else
+                               err = vmtruncate(vi, attr->ia_size);
                        if (err || ia_valid == ATTR_SIZE)
                                goto out;
                } else {
index 5c248d404f05432277be8b0d9fb147ff549578b3..f5678d5d7919029edcdfc758a28ca730afb2d2a5 100644 (file)
@@ -1021,10 +1021,17 @@ enum {
        FILE_NAME_POSIX         = 0x00,
        /* This is the largest namespace. It is case sensitive and allows all
           Unicode characters except for: '\0' and '/'.  Beware that in
-          WinNT/2k files which eg have the same name except for their case
-          will not be distinguished by the standard utilities and thus a "del
-          filename" will delete both "filename" and "fileName" without
-          warning. */
+          WinNT/2k/2003 by default files which eg have the same name except
+          for their case will not be distinguished by the standard utilities
+          and thus a "del filename" will delete both "filename" and "fileName"
+          without warning.  However if for example Services For Unix (SFU) are
+          installed and the case sensitive option was enabled at installation
+          time, then you can create/access/delete such files.
+          Note that even SFU places restrictions on the filenames beyond the
+          '\0' and '/' and in particular the following set of characters is
+          not allowed: '"', '/', '<', '>', '\'.  All other characters,
+          including the ones no allowed in WIN32 namespace are allowed.
+          Tested with SFU 3.5 (this is now free) running on Windows XP. */
        FILE_NAME_WIN32         = 0x01,
        /* The standard WinNT/2k NTFS long filenames. Case insensitive.  All
           Unicode chars except: '\0', '"', '*', '/', ':', '<', '>', '?', '\',
@@ -2367,7 +2374,9 @@ typedef struct {
  * Extended attribute flags (8-bit).
  */
 enum {
-       NEED_EA = 0x80
+       NEED_EA = 0x80          /* If set the file to which the EA belongs
+                                  cannot be interpreted without understanding
+                                  the associates extended attributes. */
 } __attribute__ ((__packed__));
 
 typedef u8 EA_FLAGS;
@@ -2375,20 +2384,20 @@ typedef u8 EA_FLAGS;
 /*
  * Attribute: Extended attribute (EA) (0xe0).
  *
- * NOTE: Always non-resident. (Is this true?)
+ * NOTE: Can be resident or non-resident.
  *
  * Like the attribute list and the index buffer list, the EA attribute value is
  * a sequence of EA_ATTR variable length records.
- *
- * FIXME: It appears weird that the EA name is not unicode. Is it true?
  */
 typedef struct {
        le32 next_entry_offset; /* Offset to the next EA_ATTR. */
        EA_FLAGS flags;         /* Flags describing the EA. */
-       u8 ea_name_length;      /* Length of the name of the EA in bytes. */
+       u8 ea_name_length;      /* Length of the name of the EA in bytes
+                                  excluding the '\0' byte terminator. */
        le16 ea_value_length;   /* Byte size of the EA's value. */
-       u8 ea_name[0];          /* Name of the EA. */
-       u8 ea_value[0];         /* The value of the EA. Immediately follows
+       u8 ea_name[0];          /* Name of the EA.  Note this is ASCII, not
+                                  Unicode and it is zero terminated. */
+       u8 ea_value[0];         /* The value of the EA.  Immediately follows
                                   the name. */
 } __attribute__ ((__packed__)) EA_ATTR;
 
index 5af3bf0b7eee15220a26841ad0812c6766720f38..29cabf93d2d24ed66ffd0aff3a5478163e93f210 100644 (file)
@@ -76,6 +76,7 @@ int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol,
  * @count:     number of clusters to allocate
  * @start_lcn: starting lcn at which to allocate the clusters (or -1 if none)
  * @zone:      zone from which to allocate the clusters
+ * @is_extension:      if TRUE, this is an attribute extension
  *
  * Allocate @count clusters preferably starting at cluster @start_lcn or at the
  * current allocator position if @start_lcn is -1, on the mounted ntfs volume
@@ -86,6 +87,13 @@ int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol,
  * @start_vcn specifies the vcn of the first allocated cluster.  This makes
  * merging the resulting runlist with the old runlist easier.
  *
+ * If @is_extension is TRUE, the caller is allocating clusters to extend an
+ * attribute and if it is FALSE, the caller is allocating clusters to fill a
+ * hole in an attribute.  Practically the difference is that if @is_extension
+ * is TRUE the returned runlist will be terminated with LCN_ENOENT and if
+ * @is_extension is FALSE the runlist will be terminated with
+ * LCN_RL_NOT_MAPPED.
+ *
  * You need to check the return value with IS_ERR().  If this is false, the
  * function was successful and the return value is a runlist describing the
  * allocated cluster(s).  If IS_ERR() is true, the function failed and
@@ -137,7 +145,8 @@ int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol,
  */
 runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn,
                const s64 count, const LCN start_lcn,
-               const NTFS_CLUSTER_ALLOCATION_ZONES zone)
+               const NTFS_CLUSTER_ALLOCATION_ZONES zone,
+               const BOOL is_extension)
 {
        LCN zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn;
        LCN prev_lcn = 0, prev_run_len = 0, mft_zone_size;
@@ -310,7 +319,7 @@ runlist_element *ntfs_cluster_alloc(ntfs_volume *vol, const VCN start_vcn,
                                continue;
                        }
                        bit = 1 << (lcn & 7);
-                       ntfs_debug("bit %i.", bit);
+                       ntfs_debug("bit 0x%x.", bit);
                        /* If the bit is already set, go onto the next one. */
                        if (*byte & bit) {
                                lcn++;
@@ -729,7 +738,7 @@ out:
        /* Add runlist terminator element. */
        if (likely(rl)) {
                rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
-               rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
+               rl[rlpos].lcn = is_extension ? LCN_ENOENT : LCN_RL_NOT_MAPPED;
                rl[rlpos].length = 0;
        }
        if (likely(page && !IS_ERR(page))) {
@@ -782,6 +791,7 @@ out:
  * @ni:                ntfs inode whose runlist describes the clusters to free
  * @start_vcn: vcn in the runlist of @ni at which to start freeing clusters
  * @count:     number of clusters to free or -1 for all clusters
+ * @ctx:       active attribute search context if present or NULL if not
  * @is_rollback:       true if this is a rollback operation
  *
  * Free @count clusters starting at the cluster @start_vcn in the runlist
@@ -791,15 +801,39 @@ out:
  * deallocated.  Thus, to completely free all clusters in a runlist, use
  * @start_vcn = 0 and @count = -1.
  *
+ * If @ctx is specified, it is an active search context of @ni and its base mft
+ * record.  This is needed when __ntfs_cluster_free() encounters unmapped
+ * runlist fragments and allows their mapping.  If you do not have the mft
+ * record mapped, you can specify @ctx as NULL and __ntfs_cluster_free() will
+ * perform the necessary mapping and unmapping.
+ *
+ * Note, __ntfs_cluster_free() saves the state of @ctx on entry and restores it
+ * before returning.  Thus, @ctx will be left pointing to the same attribute on
+ * return as on entry.  However, the actual pointers in @ctx may point to
+ * different memory locations on return, so you must remember to reset any
+ * cached pointers from the @ctx, i.e. after the call to __ntfs_cluster_free(),
+ * you will probably want to do:
+ *     m = ctx->mrec;
+ *     a = ctx->attr;
+ * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that
+ * you cache ctx->mrec in a variable @m of type MFT_RECORD *.
+ *
  * @is_rollback should always be FALSE, it is for internal use to rollback
  * errors.  You probably want to use ntfs_cluster_free() instead.
  *
- * Note, ntfs_cluster_free() does not modify the runlist at all, so the caller
- * has to deal with it later.
+ * Note, __ntfs_cluster_free() does not modify the runlist, so you have to
+ * remove from the runlist or mark sparse the freed runs later.
  *
  * Return the number of deallocated clusters (not counting sparse ones) on
  * success and -errno on error.
  *
+ * WARNING: If @ctx is supplied, regardless of whether success or failure is
+ *         returned, you need to check IS_ERR(@ctx->mrec) and if TRUE the @ctx
+ *         is no longer valid, i.e. you need to either call
+ *         ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it.
+ *         In that case PTR_ERR(@ctx->mrec) will give you the error code for
+ *         why the mapping of the old inode failed.
+ *
  * Locking: - The runlist described by @ni must be locked for writing on entry
  *           and is locked on return.  Note the runlist may be modified when
  *           needed runlist fragments need to be mapped.
@@ -807,9 +841,13 @@ out:
  *           on return.
  *         - This function takes the volume lcn bitmap lock for writing and
  *           modifies the bitmap contents.
+ *         - If @ctx is NULL, the base mft record of @ni must not be mapped on
+ *           entry and it will be left unmapped on return.
+ *         - If @ctx is not NULL, the base mft record must be mapped on entry
+ *           and it will be left mapped on return.
  */
 s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count,
-               const BOOL is_rollback)
+               ntfs_attr_search_ctx *ctx, const BOOL is_rollback)
 {
        s64 delta, to_free, total_freed, real_freed;
        ntfs_volume *vol;
@@ -839,7 +877,7 @@ s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count,
 
        total_freed = real_freed = 0;
 
-       rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, TRUE);
+       rl = ntfs_attr_find_vcn_nolock(ni, start_vcn, ctx);
        if (IS_ERR(rl)) {
                if (!is_rollback)
                        ntfs_error(vol->sb, "Failed to find first runlist "
@@ -893,7 +931,7 @@ s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn, s64 count,
 
                        /* Attempt to map runlist. */
                        vcn = rl->vcn;
-                       rl = ntfs_attr_find_vcn_nolock(ni, vcn, TRUE);
+                       rl = ntfs_attr_find_vcn_nolock(ni, vcn, ctx);
                        if (IS_ERR(rl)) {
                                err = PTR_ERR(rl);
                                if (!is_rollback)
@@ -961,7 +999,7 @@ err_out:
         * If rollback fails, set the volume errors flag, emit an error
         * message, and return the error code.
         */
-       delta = __ntfs_cluster_free(ni, start_vcn, total_freed, TRUE);
+       delta = __ntfs_cluster_free(ni, start_vcn, total_freed, ctx, TRUE);
        if (delta < 0) {
                ntfs_error(vol->sb, "Failed to rollback (error %i).  Leaving "
                                "inconsistent metadata!  Unmount and run "
index a6a8827882e73c71705ebf9ec174805e0a75dc8e..72cbca7003b2de3ee54ffe4a358efac9e767f090 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <linux/fs.h>
 
+#include "attrib.h"
 #include "types.h"
 #include "inode.h"
 #include "runlist.h"
@@ -41,16 +42,18 @@ typedef enum {
 
 extern runlist_element *ntfs_cluster_alloc(ntfs_volume *vol,
                const VCN start_vcn, const s64 count, const LCN start_lcn,
-               const NTFS_CLUSTER_ALLOCATION_ZONES zone);
+               const NTFS_CLUSTER_ALLOCATION_ZONES zone,
+               const BOOL is_extension);
 
 extern s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn,
-               s64 count, const BOOL is_rollback);
+               s64 count, ntfs_attr_search_ctx *ctx, const BOOL is_rollback);
 
 /**
  * ntfs_cluster_free - free clusters on an ntfs volume
  * @ni:                ntfs inode whose runlist describes the clusters to free
  * @start_vcn: vcn in the runlist of @ni at which to start freeing clusters
  * @count:     number of clusters to free or -1 for all clusters
+ * @ctx:       active attribute search context if present or NULL if not
  *
  * Free @count clusters starting at the cluster @start_vcn in the runlist
  * described by the ntfs inode @ni.
@@ -59,12 +62,36 @@ extern s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn,
  * deallocated.  Thus, to completely free all clusters in a runlist, use
  * @start_vcn = 0 and @count = -1.
  *
- * Note, ntfs_cluster_free() does not modify the runlist at all, so the caller
- * has to deal with it later.
+ * If @ctx is specified, it is an active search context of @ni and its base mft
+ * record.  This is needed when ntfs_cluster_free() encounters unmapped runlist
+ * fragments and allows their mapping.  If you do not have the mft record
+ * mapped, you can specify @ctx as NULL and ntfs_cluster_free() will perform
+ * the necessary mapping and unmapping.
+ *
+ * Note, ntfs_cluster_free() saves the state of @ctx on entry and restores it
+ * before returning.  Thus, @ctx will be left pointing to the same attribute on
+ * return as on entry.  However, the actual pointers in @ctx may point to
+ * different memory locations on return, so you must remember to reset any
+ * cached pointers from the @ctx, i.e. after the call to ntfs_cluster_free(),
+ * you will probably want to do:
+ *     m = ctx->mrec;
+ *     a = ctx->attr;
+ * Assuming you cache ctx->attr in a variable @a of type ATTR_RECORD * and that
+ * you cache ctx->mrec in a variable @m of type MFT_RECORD *.
+ *
+ * Note, ntfs_cluster_free() does not modify the runlist, so you have to remove
+ * from the runlist or mark sparse the freed runs later.
  *
  * Return the number of deallocated clusters (not counting sparse ones) on
  * success and -errno on error.
  *
+ * WARNING: If @ctx is supplied, regardless of whether success or failure is
+ *         returned, you need to check IS_ERR(@ctx->mrec) and if TRUE the @ctx
+ *         is no longer valid, i.e. you need to either call
+ *         ntfs_attr_reinit_search_ctx() or ntfs_attr_put_search_ctx() on it.
+ *         In that case PTR_ERR(@ctx->mrec) will give you the error code for
+ *         why the mapping of the old inode failed.
+ *
  * Locking: - The runlist described by @ni must be locked for writing on entry
  *           and is locked on return.  Note the runlist may be modified when
  *           needed runlist fragments need to be mapped.
@@ -72,11 +99,15 @@ extern s64 __ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn,
  *           on return.
  *         - This function takes the volume lcn bitmap lock for writing and
  *           modifies the bitmap contents.
+ *         - If @ctx is NULL, the base mft record of @ni must not be mapped on
+ *           entry and it will be left unmapped on return.
+ *         - If @ctx is not NULL, the base mft record must be mapped on entry
+ *           and it will be left mapped on return.
  */
 static inline s64 ntfs_cluster_free(ntfs_inode *ni, const VCN start_vcn,
-               s64 count)
+               s64 count, ntfs_attr_search_ctx *ctx)
 {
-       return __ntfs_cluster_free(ni, start_vcn, count, FALSE);
+       return __ntfs_cluster_free(ni, start_vcn, count, ctx, FALSE);
 }
 
 extern int ntfs_cluster_free_from_rl_nolock(ntfs_volume *vol,
index 590887b943f511ca964bc1c4cd30dc4cf7e0b0fd..e38e402e410351c6a70ff9ae148f10133ee28177 100644 (file)
@@ -39,8 +39,7 @@
  * If there was insufficient memory to complete the request, return NULL.
  * Depending on @gfp_mask the allocation may be guaranteed to succeed.
  */
-static inline void *__ntfs_malloc(unsigned long size,
-               gfp_t gfp_mask)
+static inline void *__ntfs_malloc(unsigned long size, gfp_t gfp_mask)
 {
        if (likely(size <= PAGE_SIZE)) {
                BUG_ON(!size);
index b011369b59561139687e627f211207dc4edff72e..0c65cbb8c5cf675af8eba7b682fac412cfa27eee 100644 (file)
@@ -49,7 +49,8 @@ static inline MFT_RECORD *map_mft_record_page(ntfs_inode *ni)
        ntfs_volume *vol = ni->vol;
        struct inode *mft_vi = vol->mft_ino;
        struct page *page;
-       unsigned long index, ofs, end_index;
+       unsigned long index, end_index;
+       unsigned ofs;
 
        BUG_ON(ni->page);
        /*
@@ -1308,7 +1309,7 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
        ll = mftbmp_ni->allocated_size;
        read_unlock_irqrestore(&mftbmp_ni->size_lock, flags);
        rl = ntfs_attr_find_vcn_nolock(mftbmp_ni,
-                       (ll - 1) >> vol->cluster_size_bits, TRUE);
+                       (ll - 1) >> vol->cluster_size_bits, NULL);
        if (unlikely(IS_ERR(rl) || !rl->length || rl->lcn < 0)) {
                up_write(&mftbmp_ni->runlist.lock);
                ntfs_error(vol->sb, "Failed to determine last allocated "
@@ -1354,7 +1355,8 @@ static int ntfs_mft_bitmap_extend_allocation_nolock(ntfs_volume *vol)
                up_write(&vol->lcnbmp_lock);
                ntfs_unmap_page(page);
                /* Allocate a cluster from the DATA_ZONE. */
-               rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE);
+               rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE,
+                               TRUE);
                if (IS_ERR(rl2)) {
                        up_write(&mftbmp_ni->runlist.lock);
                        ntfs_error(vol->sb, "Failed to allocate a cluster for "
@@ -1738,7 +1740,7 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
        ll = mft_ni->allocated_size;
        read_unlock_irqrestore(&mft_ni->size_lock, flags);
        rl = ntfs_attr_find_vcn_nolock(mft_ni,
-                       (ll - 1) >> vol->cluster_size_bits, TRUE);
+                       (ll - 1) >> vol->cluster_size_bits, NULL);
        if (unlikely(IS_ERR(rl) || !rl->length || rl->lcn < 0)) {
                up_write(&mft_ni->runlist.lock);
                ntfs_error(vol->sb, "Failed to determine last allocated "
@@ -1779,7 +1781,8 @@ static int ntfs_mft_data_extend_allocation_nolock(ntfs_volume *vol)
                        nr > min_nr ? "default" : "minimal", (long long)nr);
        old_last_vcn = rl[1].vcn;
        do {
-               rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE);
+               rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE,
+                               TRUE);
                if (likely(!IS_ERR(rl2)))
                        break;
                if (PTR_ERR(rl2) != -ENOSPC || nr == min_nr) {
@@ -1951,20 +1954,21 @@ restore_undo_alloc:
                NVolSetErrors(vol);
                return ret;
        }
-       a = ctx->attr;
-       a->data.non_resident.highest_vcn = cpu_to_sle64(old_last_vcn - 1);
+       ctx->attr->data.non_resident.highest_vcn =
+                       cpu_to_sle64(old_last_vcn - 1);
 undo_alloc:
-       if (ntfs_cluster_free(mft_ni, old_last_vcn, -1) < 0) {
+       if (ntfs_cluster_free(mft_ni, old_last_vcn, -1, ctx) < 0) {
                ntfs_error(vol->sb, "Failed to free clusters from mft data "
                                "attribute.%s", es);
                NVolSetErrors(vol);
        }
+       a = ctx->attr;
        if (ntfs_rl_truncate_nolock(vol, &mft_ni->runlist, old_last_vcn)) {
                ntfs_error(vol->sb, "Failed to truncate mft data attribute "
                                "runlist.%s", es);
                NVolSetErrors(vol);
        }
-       if (mp_rebuilt) {
+       if (mp_rebuilt && !IS_ERR(ctx->mrec)) {
                if (ntfs_mapping_pairs_build(vol, (u8*)a + le16_to_cpu(
                                a->data.non_resident.mapping_pairs_offset),
                                old_alen - le16_to_cpu(
@@ -1981,6 +1985,10 @@ undo_alloc:
                }
                flush_dcache_mft_record_page(ctx->ntfs_ino);
                mark_mft_record_dirty(ctx->ntfs_ino);
+       } else if (IS_ERR(ctx->mrec)) {
+               ntfs_error(vol->sb, "Failed to restore attribute search "
+                               "context.%s", es);
+               NVolSetErrors(vol);
        }
        if (ctx)
                ntfs_attr_put_search_ctx(ctx);
index 453d0d51ea4bc4d9b731e48345db0e45bb034b24..6c16db9e1a8a6ba2a6ce1e26f9d891f31218e7c8 100644 (file)
@@ -1447,7 +1447,7 @@ not_enabled:
        if (unlikely(i_size_read(tmp_ino) < sizeof(USN_HEADER))) {
                ntfs_error(vol->sb, "Found corrupt $UsnJrnl/$DATA/$Max "
                                "attribute (size is 0x%llx but should be at "
-                               "least 0x%x bytes).", i_size_read(tmp_ino),
+                               "least 0x%zx bytes).", i_size_read(tmp_ino),
                                sizeof(USN_HEADER));
                return FALSE;
        }
index a515e2aed829212a76fcd462bb775a9fdc8a13dd..8c454aa58ac6b157441ad2a77f9211e4a30f882e 100644 (file)
@@ -118,8 +118,7 @@ extern void release_lapic_nmi(void);
 extern void disable_timer_nmi_watchdog(void);
 extern void enable_timer_nmi_watchdog(void);
 extern void nmi_watchdog_tick (struct pt_regs * regs);
-extern int APIC_init(void);
-extern void APIC_late_time_init(void);
+extern int APIC_init_uniprocessor (void);
 extern void disable_APIC_timer(void);
 extern void enable_APIC_timer(void);
 
index 9139b89497a1aa494b63203179360124a1c3c983..622815bf3243e00306774bbea1ab01e2bc2a2f3e 100644 (file)
@@ -55,7 +55,6 @@ void init_8259A(int aeoi);
 void FASTCALL(send_IPI_self(int vector));
 void init_VISWS_APIC_irqs(void);
 void setup_IO_APIC(void);
-void IO_APIC_late_time_init(void);
 void disable_IO_APIC(void);
 void print_IO_APIC(void);
 int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn);
index d7c70c144f9fa16969286257818063329d7bf5cf..7f45f6311059f5d8e3675dbf6d0e759407ad72fb 100644 (file)
@@ -1,6 +1,11 @@
 /* two abstractions specific to kernel/smpboot.c, mainly to cater to visws
  * which needs to alter them. */
 
+static inline void smpboot_clear_io_apic_irqs(void)
+{
+       io_apic_irqs = 0;
+}
+
 static inline void smpboot_setup_warm_reset_vector(unsigned long start_eip)
 {
        CMOS_WRITE(0xa, 0xf);
@@ -27,3 +32,13 @@ static inline void smpboot_restore_warm_reset_vector(void)
 
        *((volatile long *) phys_to_virt(0x467)) = 0;
 }
+
+static inline void smpboot_setup_io_apic(void)
+{
+       /*
+        * Here we can be sure that there is an IO-APIC in the system. Let's
+        * go and set it up:
+        */
+       if (!skip_ioapic_setup && nr_ioapics)
+               setup_IO_APIC();
+}
index 14d8e0375f7ab5c408a7702944146b4759c5543a..d926471fa3597215d07817defd8866602f0cd7d3 100644 (file)
@@ -11,7 +11,14 @@ static inline void smpboot_setup_warm_reset_vector(unsigned long start_eip)
 
 /* for visws do nothing for any of these */
 
+static inline void smpboot_clear_io_apic_irqs(void)
+{
+}
+
 static inline void smpboot_restore_warm_reset_vector(void)
 {
 }
 
+static inline void smpboot_setup_io_apic(void)
+{
+}
diff --git a/include/asm-powerpc/numnodes.h b/include/asm-powerpc/numnodes.h
new file mode 100644 (file)
index 0000000..795533a
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _ASM_POWERPC_MAX_NUMNODES_H
+#define _ASM_POWERPC_MAX_NUMNODES_H
+
+/* Max 16 Nodes */
+#define NODES_SHIFT    4
+
+#endif /* _ASM_POWERPC_MAX_NUMNODES_H */
similarity index 77%
rename from include/asm-ppc64/sparsemem.h
rename to include/asm-powerpc/sparsemem.h
index c5bd47e57f179fb6d91d25fbede835af3d9f7b0e..1c95ab99deb361615522e453268d4ddedc49a489 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef _ASM_PPC64_SPARSEMEM_H
-#define _ASM_PPC64_SPARSEMEM_H 1
+#ifndef _ASM_POWERPC_SPARSEMEM_H
+#define _ASM_POWERPC_SPARSEMEM_H 1
 
 #ifdef CONFIG_SPARSEMEM
 /*
@@ -13,4 +13,4 @@
 
 #endif /* CONFIG_SPARSEMEM */
 
-#endif /* _ASM_PPC64_SPARSEMEM_H */
+#endif /* _ASM_POWERPC_SPARSEMEM_H */
index 549f44843c5ebfbb59e8a8b7d98ff5522881524d..bba5305c29ed72f6c9407be3373183f030ecd569 100644 (file)
@@ -18,7 +18,7 @@
 #define __ASM_PPC_SYS_H
 
 #include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/types.h>
 
 #if defined(CONFIG_8260)
diff --git a/include/asm-ppc64/numnodes.h b/include/asm-ppc64/numnodes.h
deleted file mode 100644 (file)
index 75ae0b9..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef _ASM_MAX_NUMNODES_H
-#define _ASM_MAX_NUMNODES_H
-
-/* Max 16 Nodes */
-#define NODES_SHIFT    4
-
-#endif /* _ASM_MAX_NUMNODES_H */
index a9e72ac3fb9fd066ebc5607bd28cfdd4ba8f010e..17cbc6db67b48390cb823916eb33bf197ff8c751 100644 (file)
@@ -396,32 +396,6 @@ extern struct device * get_device(struct device * dev);
 extern void put_device(struct device * dev);
 
 
-/* drivers/base/platform.c */
-
-struct platform_device {
-       const char      * name;
-       u32             id;
-       struct device   dev;
-       u32             num_resources;
-       struct resource * resource;
-};
-
-#define to_platform_device(x) container_of((x), struct platform_device, dev)
-
-extern int platform_device_register(struct platform_device *);
-extern void platform_device_unregister(struct platform_device *);
-
-extern struct bus_type platform_bus_type;
-extern struct device platform_bus;
-
-extern struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);
-extern int platform_get_irq(struct platform_device *, unsigned int);
-extern struct resource *platform_get_resource_byname(struct platform_device *, unsigned int, char *);
-extern int platform_get_irq_byname(struct platform_device *, char *);
-extern int platform_add_devices(struct platform_device **, int);
-
-extern struct platform_device *platform_device_register_simple(char *, unsigned int, struct resource *, unsigned int);
-
 /* drivers/base/power.c */
 extern void device_shutdown(void);
 
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
new file mode 100644 (file)
index 0000000..a726225
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * platform_device.h - generic, centralized driver model
+ *
+ * Copyright (c) 2001-2003 Patrick Mochel <mochel@osdl.org>
+ *
+ * This file is released under the GPLv2
+ *
+ * See Documentation/driver-model/ for more information.
+ */
+
+#ifndef _PLATFORM_DEVICE_H_
+#define _PLATFORM_DEVICE_H_
+
+#include <linux/device.h>
+
+struct platform_device {
+       const char      * name;
+       u32             id;
+       struct device   dev;
+       u32             num_resources;
+       struct resource * resource;
+};
+
+#define to_platform_device(x) container_of((x), struct platform_device, dev)
+
+extern int platform_device_register(struct platform_device *);
+extern void platform_device_unregister(struct platform_device *);
+
+extern struct bus_type platform_bus_type;
+extern struct device platform_bus;
+
+extern struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);
+extern int platform_get_irq(struct platform_device *, unsigned int);
+extern struct resource *platform_get_resource_byname(struct platform_device *, unsigned int, char *);
+extern int platform_get_irq_byname(struct platform_device *, char *);
+extern int platform_add_devices(struct platform_device **, int);
+
+extern struct platform_device *platform_device_register_simple(char *, unsigned int, struct resource *, unsigned int);
+
+#endif /* _PLATFORM_DEVICE_H_ */
index 317a979b24de027c572683d458f7020838901783..2b799d40d66901948b07b5cd1263e6acaed06163 100644 (file)
@@ -12,7 +12,7 @@
 #define _LINUX_SERIAL_8250_H
 
 #include <linux/serial_core.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 
 /*
  * This is the platform device platform_data structure
index 14cb2718cb77f1a0d00865ee8ff09e8456ae4fef..46e3c0bf3c946bc0cead53cfdd8cb12ae9d791c4 100644 (file)
@@ -1055,6 +1055,7 @@ typedef struct {
        unsigned char emu10k2_chip; /* Audigy 1 or Audigy 2. */
        unsigned char ca0102_chip;  /* Audigy 1 or Audigy 2. Not SB Audigy 2 Value. */
        unsigned char ca0108_chip;  /* Audigy 2 Value */
+       unsigned char ca_cardbus_chip; /* Audigy 2 ZS Notebook */
        unsigned char ca0151_chip;  /* P16V */
        unsigned char spk71;        /* Has 7.1 speakers */
        unsigned char sblive51;     /* SBLive! 5.1 - extout 0x11 -> center, 0x12 -> lfe */
index 4075d97e94b184c4d8b95a1e95057e249cf45f21..f142d403534190f4588b3d7eda585112077e0607 100644 (file)
 #endif
 #endif
 
+#ifdef CONFIG_X86_LOCAL_APIC
+#include <asm/smp.h>
+#endif
+
 /*
  * Versions of gcc older than that listed below may actually compile
  * and link okay, but the end product can have subtle run time bugs.
@@ -310,7 +314,14 @@ extern void setup_arch(char **);
 
 #ifndef CONFIG_SMP
 
+#ifdef CONFIG_X86_LOCAL_APIC
+static void __init smp_init(void)
+{
+       APIC_init_uniprocessor();
+}
+#else
 #define smp_init()     do { } while (0)
+#endif
 
 static inline void setup_per_cpu_areas(void) { }
 static inline void smp_prepare_cpus(unsigned int maxcpus) { }
index 877bb00d3295d77dccc221718753532b0783ad74..d1f9da498729c96040a477311e7c67cc94d3fc99 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/wait.h>
 #include <linux/delay.h>
index 59202de1d2ce3a236f47664e8d806762a2bc7eb8..41e224986f35e7306e5a8fda52bdf91a0860c81c 100644 (file)
@@ -28,6 +28,8 @@
 #include <linux/ctype.h>
 #include <linux/pci.h>
 #include <linux/pm.h>
+#include <linux/platform_device.h>
+
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/info.h>
index e9cd8e054f25cea7d7fccdbe5b8a2bfae65dc5dc..53aeff0b783ad059a706c84c134994723f1a614e 100644 (file)
@@ -579,6 +579,30 @@ static int __devinit snd_emu10k1_ecard_init(emu10k1_t * emu)
        return 0;
 }
 
+static int __devinit snd_emu10k1_cardbus_init(emu10k1_t * emu)
+{
+       unsigned long special_port;
+       unsigned int value;
+
+       /* Special initialisation routine
+        * before the rest of the IO-Ports become active.
+        */
+       special_port = emu->port + 0x38;
+       value = inl(special_port);
+       outl(0x00d00000, special_port);
+       value = inl(special_port);
+       outl(0x00d00001, special_port);
+       value = inl(special_port);
+       outl(0x00d0005f, special_port);
+       value = inl(special_port);
+       outl(0x00d0007f, special_port);
+       value = inl(special_port);
+       outl(0x0090007f, special_port);
+       value = inl(special_port);
+
+       return 0;
+}
+
 /*
  *  Create the EMU10K1 instance
  */
@@ -624,6 +648,16 @@ static emu_chip_details_t emu_chip_details[] = {
         .ca0108_chip = 1,
         .spk71 = 1,
         .ac97_chip = 1} ,
+       /* Audigy 2 ZS Notebook Cardbus card.*/
+       /* Tested by James@superbug.co.uk 30th October 2005 */
+       /* Not working yet, but progressing. */
+       {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102,
+        .driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]", 
+        .id = "Audigy2",
+        .emu10k2_chip = 1,
+        .ca0108_chip = 1,
+        .ca_cardbus_chip = 1,
+        .spk71 = 1} ,
        {.vendor = 0x1102, .device = 0x0008, 
         .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", 
         .id = "Audigy2",
@@ -1011,6 +1045,11 @@ int __devinit snd_emu10k1_create(snd_card_t * card,
                        snd_emu10k1_free(emu);
                        return err;
                }
+       } else if (emu->card_capabilities->ca_cardbus_chip) {
+               if ((err = snd_emu10k1_cardbus_init(emu)) < 0) {
+                       snd_emu10k1_free(emu);
+                       return err;
+               }
        } else {
                /* 5.1: Enable the additional AC97 Slots. If the emu10k1 version
                        does not support this, it shouldn't do any harm */